Vite 插件 API 獨(dú)有鉤子

2023-02-17 11:40 更新

Vite 插件也可以提供鉤子來服務(wù)于特定的 Vite 目標(biāo)。這些鉤子會(huì)被 Rollup 忽略

config

  • 類型: ?(config: UserConfig, env: { mode: string, command: string }) => UserConfig | null | void?
  • 種類: ?async?, ?sequential?

在解析 Vite 配置前調(diào)用。鉤子接收原始用戶配置(命令行選項(xiàng)指定的會(huì)與配置文件合并)和一個(gè)描述配置環(huán)境的變量,包含正在使用的 mode 和 command。它可以返回一個(gè)將被深度合并到現(xiàn)有配置中的部分配置對(duì)象,或者直接改變配置(如果默認(rèn)的合并不能達(dá)到預(yù)期的結(jié)果)。

示例:

// 返回部分配置(推薦)
const partialConfigPlugin = () => ({
  name: 'return-partial',
  config: () => ({
    alias: {
      foo: 'bar'
    }
  })
})

// 直接改變配置(應(yīng)僅在合并不起作用時(shí)使用)
const mutateConfigPlugin = () => ({
  name: 'mutate-config',
  config(config, { command }) {
    if (command === 'build') {
      config.root = __dirname
    }
  }
})

注意:

用戶插件在運(yùn)行這個(gè)鉤子之前會(huì)被解析,因此在 config 鉤子中注入其他插件不會(huì)有任何效果。

configResolved

  • 類型: ?(config: ResolvedConfig) => void | Promise<void>?
  • 種類: ?async?, ?parallel?

在解析 Vite 配置后調(diào)用。使用這個(gè)鉤子讀取和存儲(chǔ)最終解析的配置。當(dāng)插件需要根據(jù)運(yùn)行的命令做一些不同的事情時(shí),它也很有用。

示例:

const exmaplePlugin = () => {
  let config

  return {
    name: 'read-config',

    configResolved(resolvedConfig) {
      // 存儲(chǔ)最終解析的配置
      config = resolvedConfig
    },

    // 在其他鉤子中使用存儲(chǔ)的配置
    transform(code, id) {
      if (config.command === 'serve') {
        // dev: 由開發(fā)服務(wù)器調(diào)用的插件
      } else {
        // build: 由 Rollup 調(diào)用的插件
      }
    }
  }
}

注意,在開發(fā)環(huán)境下,command 的值為 serve(在 CLI 中,vite 和 vite dev 是 vite serve 的別名)。

configureServer

  • 類型:?(server: ViteDevServer) => (() => void) | void | Promise<(() => void) | void>?
  • 種類: ?async?, ?sequential?

是用于配置開發(fā)服務(wù)器的鉤子。最常見的用例是在內(nèi)部 connect 應(yīng)用程序中添加自定義中間件:

const myPlugin = () => ({
  name: 'configure-server',
  configureServer(server) {
    server.middlewares.use((req, res, next) => {
      // 自定義請(qǐng)求處理...
    })
  }
})

注入后置中間件

configureServer 鉤子將在內(nèi)部中間件被安裝前調(diào)用,所以自定義的中間件將會(huì)默認(rèn)會(huì)比內(nèi)部中間件早運(yùn)行。如果你想注入一個(gè)在內(nèi)部中間件之后運(yùn)行的中間件,你可以從 configureServer 返回一個(gè)函數(shù),將會(huì)在內(nèi)部中間件安裝后被調(diào)用:

const myPlugin = () => ({
  name: 'configure-server',
  configureServer(server) {
    // 返回一個(gè)在內(nèi)部中間件安裝后
    // 被調(diào)用的后置鉤子
    return () => {
      server.middlewares.use((req, res, next) => {
        // 自定義請(qǐng)求處理...
      })
    }
  }
})

存儲(chǔ)服務(wù)器訪問

在某些情況下,其他插件鉤子可能需要訪問開發(fā)服務(wù)器實(shí)例(例如訪問 websocket 服務(wù)器、文件系統(tǒng)監(jiān)視程序或模塊圖)。這個(gè)鉤子也可以用來存儲(chǔ)服務(wù)器實(shí)例以供其他鉤子訪問:

const myPlugin = () => {
  let server
  return {
    name: 'configure-server',
    configureServer(_server) {
      server = _server
    },
    transform(code, id) {
      if (server) {
        // 使用 server...
      }
    }
  }
}

注意 configureServer 在運(yùn)行生產(chǎn)版本時(shí)不會(huì)被調(diào)用,所以其他鉤子需要防范它缺失。

transformIndexHtml

  • 類型: ?IndexHtmlTransformHook | { enforce?: 'pre' | 'post', transform: IndexHtmlTransformHook }?
  • 種類: ?async?, ?sequential?

轉(zhuǎn)換 ?index.html? 的專用鉤子。鉤子接收當(dāng)前的 HTML 字符串和轉(zhuǎn)換上下文。上下文在開發(fā)期間暴露?ViteDevServer?實(shí)例,在構(gòu)建期間暴露 Rollup 輸出的包。

這個(gè)鉤子可以是異步的,并且可以返回以下其中之一:

  • 經(jīng)過轉(zhuǎn)換的 HTML 字符串
  • 注入到現(xiàn)有 HTML 中的標(biāo)簽描述符對(duì)象數(shù)組(?{ tag, attrs, children }?)。每個(gè)標(biāo)簽也可以指定它應(yīng)該被注入到哪里(默認(rèn)是在 ?<head>? 之前)
  • 一個(gè)包含 ?{ html, tags }? 的對(duì)象

基礎(chǔ)示例:

const htmlPlugin = () => {
  return {
    name: 'html-transform',
    transformIndexHtml(html) {
      return html.replace(
        /<title>(.*?)<\/title>/,
        `<title>Title replaced!</title>`
      )
    }
  }
}

完整鉤子簽名:

type IndexHtmlTransformHook = (
  html: string,
  ctx: {
    path: string
    filename: string
    server?: ViteDevServer
    bundle?: import('rollup').OutputBundle
    chunk?: import('rollup').OutputChunk
  }
) =>
  | IndexHtmlTransformResult
  | void
  | Promise<IndexHtmlTransformResult | void>

type IndexHtmlTransformResult =
  | string
  | HtmlTagDescriptor[]
  | {
      html: string
      tags: HtmlTagDescriptor[]
    }

interface HtmlTagDescriptor {
  tag: string
  attrs?: Record<string, string>
  children?: string | HtmlTagDescriptor[]
  /**
   * 默認(rèn): 'head-prepend'
   */
  injectTo?: 'head' | 'body' | 'head-prepend' | 'body-prepend'
}

handleHotUpdate

類型: ?(ctx: HmrContext) => Array<ModuleNode> | void | Promise<Array<ModuleNode> | void>?

執(zhí)行自定義 HMR 更新處理。鉤子接收一個(gè)帶有以下簽名的上下文對(duì)象:

interface HmrContext {
  file: string
  timestamp: number
  modules: Array<ModuleNode>
  read: () => string | Promise<string>
  server: ViteDevServer
}
  • ?modules ?是受更改文件影響的模塊數(shù)組。它是一個(gè)數(shù)組,因?yàn)閱蝹€(gè)文件可能映射到多個(gè)服務(wù)模塊(例如 Vue 單文件組件)。
  • ?read ?這是一個(gè)異步讀函數(shù),它返回文件的內(nèi)容。之所以這樣做,是因?yàn)樵谀承┫到y(tǒng)上,文件更改的回調(diào)函數(shù)可能會(huì)在編輯器完成文件更新之前過快地觸發(fā),并 ?fs.readFile? 直接會(huì)返回空內(nèi)容。傳入的 ?read ?函數(shù)規(guī)范了這種行為。

鉤子可以選擇:

  • 過濾和縮小受影響的模塊列表,使 HMR 更準(zhǔn)確。
  • 返回一個(gè)空數(shù)組,并通過向客戶端發(fā)送自定義事件來執(zhí)行完整的自定義 HMR 處理:
handleHotUpdate({ server }) {
  server.ws.send({
    type: 'custom',
    event: 'special-update',
    data: {}
  })
  return []
}

客戶端代碼應(yīng)該使用 ?HMR API? 注冊(cè)相應(yīng)的處理器(這應(yīng)該被相同插件的 transform 鉤子注入):

if (import.meta.hot) {
  import.meta.hot.on('special-update', (data) => {
    // 執(zhí)行自定義更新
  })
}


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)