插件系统
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-aliasimport json from '@rollup/plugin-json'
import alias from '@rollup/plugin-alias'
export default defineConfig({
plugins: [
json(), // JSON 导入
alias({ entries: { '@': './src' } }), // 路径别名
],
})不兼容的情况:依赖 Rollup 特定构建钩子且 Vite 不支持时,需要用 Vite 特有的钩子重写。