Skip to content

本节内容基于Vite插件API

vite 的插件机制是基于 Rollup 生态的。也就是说大多数兼容情况下Rollup 插件能够直接用于 vite 工程中。

您可以查看完整的Rollup插件文档

定义与使用

一个插件的简单定义与使用方式如下:

js
// myPlugin.js
const fileRegex = /\.(my-file-ext)$/

export default function myPlugin() {
  return {
    name: 'transform-file',

    transform(src, id) {
      if (fileRegex.test(id)) {
        return {
          code: compileFileToJS(src),
          map: null // 如果可行将提供 source map
        }
      }
    }
  }
}
js
// vite.config.js
import { defineConfig } from 'vite'
import myPlugin from 'myPlugin.js'

export default defineConfig({
  plugins: [myPlugin()]
})

通用钩子

正如上文所述,vite 插件机制是基于 Rollup 的。因此二者具有一些通用钩子

以下钩子在服务器启动时被调用:

  • options
  • buildStart

以下钩子会在每个传入模块请求时被调用:

  • resolveId
  • load
  • transform

以下钩子在服务器关闭时被调用:

  • buildEnd
  • closeBundle

TIP

请注意 moduleParsed 钩子在开发中是不会被调用的,因为 Vite 为了性能会避免完整的 AST 解析。

特殊钩子

Vite 在通用钩子的基础上,自定义一些内置钩子供开发者使用。这类特殊钩子,会被 Rollup 忽略。

config

  • 类型:(config: UserConfig, env: { mode: string, command: string }) => UserConfig | null | void

该钩子在解析 Vite 配置前调用

钩子接收原始用户配置(命令行选项指定的会与配置文件合并)和一个描述配置环境的变量,包含正在使用的 modecommand

它可以返回一个将被深度合并到现有配置中的部分配置对象,或者直接改变配置(如果默认的合并不能达到预期的结果)。

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

// 直接改变配置(应仅在合并不起作用时使用)
const mutateConfigPlugin = () => ({
  name: 'mutate-config',
  config(config, { command }) {
    if (command === 'build') {
      config.root = 'foo'
    }
  }
})

configResolved

  • 类型:(config: ResolvedConfig) => void | Promise<void>

在解析 Vite 配置后调用

使用这个钩子读取和存储最终解析的配置。当插件需要根据运行的命令做一些不同的事情时,它也很有用。

js
const examplePlugin = () => {
  let config

  return {
    name: 'read-config',

    configResolved(resolvedConfig) {
      // 存储最终解析的配置
      config = resolvedConfig
    },

    // 在其他钩子中使用存储的配置
    transform(code, id) {
      if (config.command === 'serve') {
        // dev: 由开发服务器调用的插件
      } else {
        // build: 由 Rollup 调用的插件
      }
    }
  }
}

configureServer

  • 类型:(server: ViteDevServer) => (() => void) | void | Promise<(() => void) | void>

该钩子可用于配置开发服务器

js
const myPlugin = () => ({
  name: 'configure-server',
  configureServer(server) {
    server.middlewares.use((req, res, next) => {
      // 自定义请求处理...
    })
  }
})

存储服务器实例,供其他钩子使用:

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

configurePreviewServer

  • 类型:(server: { middlewares: Connect.Server, httpServer: http.Server }) => (() => void) | void | Promise<(() => void) | void>

该钩子可用于配置预览服务器

js
const myPlugin = () => ({
  name: 'configure-preview-server',
  configurePreviewServer(server) {
    // 返回一个钩子,会在其他中间件安装完成后调用
    return () => {
      server.middlewares.use((req, res, next) => {
        // 自定义处理请求 ...
      })
    }
  }
})

transformIndexHtml

  • 类型:IndexHtmlTransformHook | { enforce?: 'pre' | 'post', transform: IndexHtmlTransformHook }

该钩子可用于转换 index.html

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

handleHotUpdate

  • 类型:(ctx: HmrContext) => Array<ModuleNode> | void | Promise<Array<ModuleNode> | void>

该钩子执行自定义 HMR 更新处理

js
// 插件注册钩子
handleHotUpdate({ server }) {
  server.ws.send({
    type: 'custom',
    event: 'special-update',
    data: {}
  })
  return []
}
js
// 客户端处理监听
if (import.meta.hot) {
  import.meta.hot.on('special-update', (data) => {
    // 执行自定义更新
  })
}

插件顺序

Vite 插件支持配置 enforce 属性来自定义执行顺序。完整的执行顺序如下:

  • Alias
  • 带有 enforce: 'pre' 的用户插件
  • Vite 核心插件
  • 没有 enforce 值的用户插件
  • Vite 构建用的插件
  • 带有 enforce: 'post' 的用户插件
  • Vite 后置构建插件(最小化,manifest,报告)

插件场景

默认情况下插件在开发(serve)和构建(build)模式中都会调用。

如果插件只需要在预览或构建期间有条件地应用,可以使用 apply 属性指明仅在 'build''serve' 模式时调用。

js
function myPlugin() {
  return {
    name: 'build-only',
    apply: 'build' // 或 'serve'
  }
}

虚拟模块

Vite 插件中支持定义虚拟模块

在插件中定义虚拟模块:

js
// myPlugin.js
export default function myPlugin() {
  const virtualModuleId = 'virtual:my-module'
  const resolvedVirtualModuleId = '\0' + virtualModuleId

  return {
    name: 'my-plugin', // 必须的,将会在 warning 和 error 中显示
    resolveId(id) {
      if (id === virtualModuleId) {
        return resolvedVirtualModuleId
      }
    },
    load(id) {
      if (id === resolvedVirtualModuleId) {
        return `export const msg = "from virtual module"`
      }
    }
  }
}

然后就可以在代码中引入虚拟模块:

js
// main.js
import { msg } from 'virtual:my-module'

console.log(msg)