Skip to content

插件

Hooks

javascript
export default function vitePlugin() {
  // 定义vite插件唯一id
  const virtualFileId = '@my-virtual-plugin'
  // 返回插件对象
  return {
    // 必须的,将会显示在 warning 和 error 中
    name: 'vite-plugin',

    // *以下钩子函数按照实际执行顺序排列*

    /**
     * config 可以在被解析之前修改 Vite 配置
     * Vite独有钩子
     * https://cn.vitejs.dev/guide/api-plugin.html#config
     * @param config vite配置信息
     * @param env 描述配置环境的变量
     */
    config: (config, env) => ({}),

    /**
     * configResolved 解析 Vite 配置后调用,使用这个钩子读取和存储最终解析的配置
     * Vite独有钩子
     * https://cn.vitejs.dev/guide/api-plugin.html#configresolved
     * @param config vite配置信息
     */
    configResolved: config => ({}),

    /**
     * options 替换或操作传递给rollup.rollup()的选项
     * 通用钩子
     * https://rollupjs.org/guide/en/#options
     * @param options rollup配置信息
     */
    options: options => ({}),

    /**
     * configureServer 用于配置开发服务器
     * Vite独有钩子
     * https://cn.vitejs.dev/guide/api-plugin.html#configureserver
     * @param server ViteDevServer配置信息
     * https://cn.vitejs.dev/guide/api-javascript.html#vitedevserver
     */
    configureServer: server => ({}),

    /**
     * buildStart 在每个rollup.rollup()构建时被调用
     * 通用钩子
     * https://rollupjs.org/guide/en/#buildstart
     * @param options rollup配置信息
     */
    buildStart: options => ({}),

    /**
     * 此时 Vite dev server is running
     */

    /**
     * transformIndexHtml 转换 index.html 的专用钩子
     * Vite独有钩子
     * https://cn.vitejs.dev/guide/api-plugin.html#transformindexhtml
     * @param html html字符串
     * @param ctx 转换上下文; 在开发期间会额外暴露ViteDevServer实例; 在构建期间会额外暴露Rollup输出的包
     */
    transformIndexHtml: (html, ctx) => ({}),

    /**
     * resolveId 用户自定义解析器
     * 通用钩子 会在每个传入模块请求时被调用
     * https://rollupjs.org/guide/en/#resolveid
     * @param source 源导入者 例子: import { foo } from '../bar.js', '../bar.js' 为source
     * @param importer 导入者所在文件绝对路径
     */
    resolveId: (source, importer) => ({}),

    /**
     * load 用户自定义加载器
     * 通用钩子 会在每个传入模块请求时被调用
     * https://rollupjs.org/guide/en/#load
     * @param id 同resolveId source
     */
    load: id => ({}),

    /**
     * transform 可以用来转换单个模块
     * 通用钩子 会在每个传入模块请求时被调用
     * https://rollupjs.org/guide/en/#transform
     * @param code 模块代码
     * @param id 同resolveId source
     */
    transform: (code, id) => ({}),

    /**
     * buildEnd 在 Rollup 完成产物但尚未调用 generate 或 write 之前调用;也可以返回一个 Promise。如果在构建过程中发生错误,则将其传递给此钩子。
     * 通用钩子
     */
    buildEnd: () => ({}),

    /**
     * closeBundle 构建时被调用
     * 通用钩子
     */
    closeBundle: () => ({}),
  }
}

插件预设

plugins 也可以接受将多个插件作为单个元素的预设。这对于使用多个插件实现的复杂特性(如框架集成)很有用。该数组将在内部被扁平化(flatten)。

js
import frameworkDevtools from 'vite-plugin-framework-devtools'
import frameworkRefresh from 'vite-plugin-framework-refresh'

export default function framework(config) {
  return [frameworkRefresh(config), frameworkDevTools(config)]
}
js
import { defineConfig } from 'vite'
import framework from 'vite-plugin-framework'

export default defineConfig({
  plugins: [framework()],
})

强制插件排序

为了与某些 Rollup 插件兼容,可能需要强制修改插件的执行顺序

一个 Vite 插件可以额外指定一个 enforce 属性来调整它的应用顺序。enforce 的值可以是 pre 或 post。解析后的插件将按照以下顺序排列:

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

按需应用

默认情况下插件在开发 (serve) 和生产 (build) 模式中都会调用。如果插件在服务或构建期间按需使用,请使用 apply 属性指明它们仅在 'build' 或 'serve' 模式时调用:

javascript
import typescript2 from 'rollup-plugin-typescript2'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [
    {
      ...typescript2(),
      apply: 'build',
    },
  ],
})

过滤与 include/exclude 模式

Vite 暴露了 @rollup/pluginutils 的 createFilter 函数,以支持 Vite 独有插件和集成使用标准的 include/exclude 过滤模式。

ts
import type { FilterPattern, Plugin } from 'vite'

import { createFilter } from 'vite'

export interface Options {
  include?: FilterPattern
  exclude?: FilterPattern
}

export function vitePlugin(options: Options = {}): Plugin {
  const { include, exclude } = options
  const filter = createFilter(include || /\.[jt]sx$/, exclude)

  return {
    name: 'vite-plugin',

    async transform(code, id, opt) {
      if (filter(id)) {
        // ...
      }
    },
  }
}

虚拟模块

虚拟模块是一种很实用的模式,使你可以对使用 ESM 语法的源文件传入一些编译时信息。

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"`
    },
  }
}

这使得可以在 JavaScript 中引入这些模块:

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

console.log(msg)

虚拟模块在 Vite(以及 Rollup)中都以 virtual: 为前缀,作为面向用户路径的一种约定。如果可能的话,插件名应该被用作命名空间,以避免与生态系统中的其他插件发生冲突。举个例子,vite-plugin-posts 可以要求用户导入一个 virtual:posts 或者 virtual:posts/helpers 虚拟模块来获得编译时信息。在内部,使用了虚拟模块的插件在解析时应该将模块 ID 加上前缀 \0,这一约定来自 rollup 生态。这避免了其他插件尝试处理这个 ID(比如 node 解析),而例如 sourcemap 这些核心功能可以利用这一信息来区别虚拟模块和正常文件。\0 在导入 URL 中不是一个被允许的字符,因此我们需要在导入分析时替换掉它们。一个虚拟 ID 为 \0{id} 在浏览器中开发时,最终会被编码为 /@id/__x00__{id}。这个 id 会被解码回进入插件处理管线前的样子,因此这对插件钩子的代码是不可见的。

请注意,直接从真实文件派生出来的模块,就像单文件组件中的脚本模块(如.vue 或 .svelte SFC)不需要遵循这个约定。SFC 通常在处理时生成一组子模块,但这些模块中的代码可以映射回文件系统。对这些子模块使用 \0 会使 sourcemap 无法正常工作。