Skip to content
插件系统

插件系统

Vite 的插件 API 继承自 Rollup,并添加了 Vite 独有的钩子和功能。绝大多数 Rollup 插件可以直接在 Vite 中使用。


插件使用

// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import react from '@vitejs/plugin-react'

export default defineConfig({
  plugins: [
    vue(),            // Vue SFC 支持
    react(),          // React Fast Refresh
    // 插件可以按条件引入
    // ...(isDev ? [devPlugin()] : []),
  ],
})

常用官方插件

插件 用途 npm 包
Vue 3 SFC .vue 文件编译 @vitejs/plugin-vue
Vue JSX Vue JSX/TSX 支持 @vitejs/plugin-vue-jsx
React Fast Refresh + JSX 转换 @vitejs/plugin-react
Legacy 旧浏览器兼容(IE11+) @vitejs/plugin-legacy

@vitejs/plugin-react 配置

import react from '@vitejs/plugin-react'

export default defineConfig({
  plugins: [
    react({
      // 使用 Babel(默认使用 esbuild,速度更快)
      babel: {
        plugins: ['styled-components'],
        babelrc: true,
      },
      // JSX 运行时
      jsxRuntime: 'automatic',  // 'classic' | 'automatic'(默认)
      // JSX 导入源(automatic 模式下的 import 来源)
      jsxImportSource: '@emotion/react',
      // Fast Refresh 排除
      exclude: [/node_modules/],
    }),
  ],
})

@vitejs/plugin-vue 配置

import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [
    vue({
      // reactivityTransform: true,        // 已废弃
      template: {
        compilerOptions: {
          isCustomElement: (tag) => tag.startsWith('app-'),
        },
      },
    }),
  ],
})

常用社区插件

插件 说明
vite-plugin-svgr SVG 作为 React 组件导入
vite-plugin-pwa PWA 支持(Service Worker + manifest)
vite-plugin-compression 构建时生成 gzip/brotli 文件
vite-plugin-inspect 查看 Vite 插件和模块的中间状态
vite-plugin-html HTML 压缩与 EJS 模板
vite-plugin-imagemin 图片压缩
vite-plugin-checker 在浏览器中显示 TypeScript / ESLint 错误
vite-plugin-mock Mock 数据服务
vite-plugin-pages 基于文件系统的路由生成
unplugin-auto-import 自动导入 API(Vue/React Hooks 等)
unplugin-vue-components Vue 组件自动按需导入
vite-plugin-monaco-editor Monaco Editor 集成
rollup-plugin-visualizer 构建产物体积分析

插件 API 概述

Vite 独有钩子

Vite 在 Rollup 插件钩子的基础上增加了以下独有钩子:

钩子 类型 调用时机 典型用途
config 同步/异步 解析 Vite 配置前 修改或扩展配置
configResolved 同步 Vite 配置解析完成后 读取最终配置,记录日志
configureServer 异步 创建开发服务器时 添加自定义中间件
configurePreviewServer 异步 创建预览服务器时 自定义预览中间件
transformIndexHtml 异步 HTML 入口文件转换时 注入标签/脚本
handleHotUpdate 异步 HMR 更新时 自定义 HMR 处理逻辑

Rollup 构建钩子(Vite 兼容)

钩子 阶段 说明
options 构建开始 修改 Rollup 选项
buildStart 构建开始 构建开始时的清理/准备工作
resolveId 解析 自定义模块 ID 解析
load 加载 自定义模块加载
transform 转换 模块内容转换(最常用)
buildEnd 构建结束 构建结束后的清理
closeBundle Bundle 关闭 Bundle 写入完毕

钩子执行顺序

开发服务器启动流程:
  config → configResolved → configureServer → ...

构建流程:
  config → configResolved → options → buildStart
    → resolveId → load → transform → ...(每个模块)
    → buildEnd → closeBundle

HMR 更新流程:
  handleHotUpdate → ...

编写自定义插件

最简插件

// vite.config.js
function myPlugin() {
  return {
    name: 'my-plugin',          // 必填:插件名称
    enforce: 'pre',             // 可选:'pre' | 'post',调整执行顺序
    apply: 'build',             // 可选:'serve' | 'build',限定模式

    // 转换代码
    transform(code, id) {
      if (id.endsWith('.js')) {
        return code.replace(/__REPLACE_ME__/g, 'replaced')
      }
    },
  }
}

实用示例:注入全局变量

// 一个在构建时替换环境占位符的插件
function injectVersion() {
  let version = ''

  return {
    name: 'inject-version',
    configResolved(config) {
      // 读取 package.json 版本号
      const pkg = JSON.parse(
        require('fs').readFileSync('./package.json', 'utf-8')
      )
      version = pkg.version
    },
    transform(code, id) {
      if (id.endsWith('.js') || id.endsWith('.ts')) {
        return code.replace(/__APP_VERSION__/g, `"${version}"`)
      }
    },
  }
}

实用示例:虚拟模块

// 创建一个虚拟模块(内存中的模块)
function virtualModule() {
  const virtualModuleId = 'virtual:my-module'
  const resolvedVirtualModuleId = '\0' + virtualModuleId

  return {
    name: 'virtual-module',
    resolveId(id) {
      if (id === virtualModuleId) {
        return resolvedVirtualModuleId
      }
    },
    load(id) {
      if (id === resolvedVirtualModuleId) {
        return `export const msg = "This is a virtual module!"`
      }
    },
  }
}
// 在其他文件中使用虚拟模块
import { msg } from 'virtual:my-module'
console.log(msg) // "This is a virtual module!"

🔬 深入原理:虚拟模块不存储在磁盘上,完全在内存中生成。\0 前缀是 Rollup 的约定,表示这是一个内部虚拟模块(会阻止其他插件误处理)。

实用示例:自定义 HMR

function customHMR() {
  return {
    name: 'custom-hmr',
    handleHotUpdate({ file, server, modules }) {
      // 当自定义文件类型变化时,也刷新相关模块
      if (file.endsWith('.custom')) {
        // 找到需要更新的模块
        const affectedModules = server.moduleGraph.getModulesByFile(
          file.replace('.custom', '.js')
        )
        // 返回需要更新的模块列表
        return [...modules, ...(affectedModules || [])]
      }
      // 返回 undefined 使用默认行为
    },
  }
}

enforce 和 apply

export default function myPlugin() {
  return {
    name: 'my-plugin',

    // 执行顺序
    // 'pre'  → 在核心插件之前执行
    // 'post' → 在核心插件之后执行
    // 不设置 → 默认(中间)
    enforce: 'pre',

    // 限定应用场景
    // 'serve' → 只在开发服务器时加载
    // 'build' → 只在生产构建时加载
    // undefined → 不限定
    // 也可以是函数:apply(config, { command }) { return command === 'build' }
    apply: 'build',
  }
}

插件排序规则

执行顺序(按 enforce 排序):

1. enforce: 'pre' 的插件(Alias 解析等)
2. 没有 enforce 的普通插件
3. Vite 核心插件
4. enforce: 'post' 的插件(后处理等)
5. Vite 构建后插件(压缩、分析等)

💡 最佳实践:大多数自定义插件不需要设置 enforce。只有需要在核心插件之前(如代码转换预处理)或之后(如构建分析)执行时才需要。


移植 Rollup 插件

大部分 Rollup 插件可以直接使用:

npm i -D @rollup/plugin-json @rollup/plugin-alias
import json from '@rollup/plugin-json'
import alias from '@rollup/plugin-alias'

export default defineConfig({
  plugins: [
    json(),                             // JSON 导入
    alias({ entries: { '@': './src' } }), // 路径别名
  ],
})

不兼容的情况:依赖 Rollup 特定构建钩子且 Vite 不支持时,需要用 Vite 特有的钩子重写。