Vite 插件开发实战:打造属于你的构建工具
·
一、为什么要学 Vite 插件开发?
在使用 Vite 的过程中,你可能会遇到这些场景:
- 需要在构建时自动生成某些文件
- 想要自定义模块解析逻辑
- 需要在开发服务器中添加特定的 API 路由
- 想要集成特定的代码检查或转换工具
Vite 插件就是解决这些问题的钥匙!
Vite 插件 vs Rollup 插件
Vite 的插件系统基于 Rollup,但做了大量扩展:
| 特性 | Rollup 插件 | Vite 插件 |
|---|---|---|
| 构建阶段 | ✅ 支持 | ✅ 支持 |
| 开发服务器 | ❌ 不支持 | ✅ 支持 |
| HMR 热更新 | ❌ 不支持 | ✅ 支持 |
| 配置解析 | ❌ 不支持 | ✅ 支持 |
二、插件基础:Hello World
Vite 插件本质上是一个对象(或返回对象的函数),包含 name 属性和各种钩子函数:
2.1 最简单的插件
// my-first-plugin.js
export default function myFirstPlugin() {
return {
name: 'my-first-plugin',
// 配置解析时调用
config(config, { command }) {
console.log('🚀 Vite 模式:', command); // 'serve' 或 'build'
return {
// 可以返回部分配置,会与用户配置合并
base: command === 'build' ? '/app/' : '/'
};
},
// 构建开始时调用
buildStart() {
console.log('📦 开始构建...');
},
// 构建结束时调用
buildEnd() {
console.log('✅ 构建完成!');
}
};
}
使用插件:
// vite.config.js
import { defineConfig } from 'vite';
import myFirstPlugin from './my-first-plugin.js';
export default defineConfig({
plugins: [myFirstPlugin()]
});
2.2 运行效果
$ npm run dev
🚀 Vite 模式: serve
📦 开始构建...
✅ 构建完成!
VITE v5.0.0 ready in 320 ms
三、核心钩子详解
Vite 提供了丰富的钩子函数,覆盖了从配置解析到构建完成的整个生命周期。
3.1 配置相关钩子
export default function configPlugin() {
return {
name: 'config-plugin',
// 1. config - 修改或扩展配置
config(userConfig, { command, mode }) {
// command: 'serve' | 'build'
// mode: 'development' | 'production' | 自定义
return {
resolve: {
alias: {
'@': '/src'
}
}
};
},
// 2. configResolved - 配置解析完成后
configResolved(resolvedConfig) {
// 可以获取最终解析后的配置
console.log('📁 项目根目录:', resolvedConfig.root);
console.log('🔧 运行模式:', resolvedConfig.mode);
}
};
}
3.2 开发服务器钩子
这是 Vite 独有的,Rollup 插件无法使用:
export default function serverPlugin() {
return {
name: 'server-plugin',
// 配置开发服务器
configureServer(server) {
// server 是 ViteDevServer 实例
// 添加自定义路由
server.middlewares.use('/api/health', (req, res, next) => {
res.end(JSON.stringify({ status: 'ok', time: Date.now() }));
});
// 监听文件变化
server.watcher.on('change', (file) => {
console.log('📝 文件变化:', file);
});
},
// 配置预览服务器(vite preview)
configurePreviewServer(server) {
server.middlewares.use('/api/version', (req, res) => {
res.end(JSON.stringify({ version: '1.0.0' }));
});
}
};
}
3.3 构建钩子(Rollup 兼容)
export default function buildPlugin() {
return {
name: 'build-plugin',
// 解析模块ID
resolveId(source, importer) {
if (source === 'virtual-module') {
return source; // 返回解析后的ID
}
},
// 加载模块内容
load(id) {
if (id === 'virtual-module') {
return 'export const msg = "Hello from virtual module!"';
}
},
// 转换代码
transform(code, id) {
// 只对 .js 文件进行处理
if (id.endsWith('.js')) {
// 简单的代码转换示例
return {
code: code.replace(/console\\.log/g, 'console.debug'),
map: null // 可以返回 source map
};
}
}
};
}
3.4 完整生命周期图
config → configResolved → configureServer
↓
(开发服务器运行中)
↓
buildStart → resolveId → load → transform
↓
(HMR 触发时重复)
↓
buildEnd → closeBundle
四、实战案例
案例 1:自动生成环境信息文件
开发中经常需要知道当前构建的时间、版本号等信息:
// plugins/build-info-plugin.js
import { writeFileSync } from 'fs';
import { resolve } from 'path';
export default function buildInfoPlugin() {
return {
name: 'build-info',
buildStart() {
const info = {
version: process.env.npm_package_version || '1.0.0',
buildTime: new Date().toISOString(),
nodeEnv: process.env.NODE_ENV,
platform: process.platform
};
// 将信息写入 JSON 文件
const outputPath = resolve(process.cwd(), 'public', 'build-info.json');
writeFileSync(outputPath, JSON.stringify(info, null, 2));
console.log('📝 构建信息已生成');
}
};
}
使用:
// vite.config.js
import buildInfoPlugin from './plugins/build-info-plugin.js';
export default {
plugins: [buildInfoPlugin()]
};
前端获取:
// App.vue 或任意 JS 文件
fetch('/build-info.json')
.then(r => r.json())
.then(info => {
console.log('📦 版本:', info.version);
console.log('🕐 构建时间:', info.buildTime);
});
案例 2:Mock API 服务器
开发时常用的 Mock 数据插件:
// plugins/mock-plugin.js
import { readFileSync } from 'fs';
import { resolve } from 'path';
const mockData = {
'/api/users': [
{ id: 1, name: '张三', role: 'admin' },
{ id: 2, name: '李四', role: 'user' }
],
'/api/posts': [
{ id: 1, title: 'Hello Vite', content: 'Vite 真快!' }
]
};
export default function mockPlugin(options = {}) {
const { prefix = '/api', delay = 500 } = options;
return {
name: 'mock-server',
configureServer(server) {
server.middlewares.use((req, res, next) => {
// 只处理 API 请求
if (!req.url.startsWith(prefix)) {
return next();
}
// 模拟网络延迟
setTimeout(() => {
const data = mockData[req.url];
if (data) {
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ code: 0, data }));
} else {
res.statusCode = 404;
res.end(JSON.stringify({ code: 404, msg: 'Not Found' }));
}
}, delay);
});
}
};
}
使用:
// vite.config.js
import mockPlugin from './plugins/mock-plugin.js';
export default {
plugins: [
mockPlugin({
prefix: '/api',
delay: 300 // 模拟 300ms 延迟
})
]
};
案例 3:条件编译(类似 #ifdef)
实现类似 C 语言的条件编译功能:
// plugins/conditional-compile-plugin.js
export default function conditionalCompilePlugin(options = {}) {
const { env = {} } = options;
return {
name: 'conditional-compile',
transform(code, id) {
// 只对 JS/TS/Vue 文件处理
if (!/\\.(js|ts|vue)$/.test(id)) return;
// 匹配 //#ifdef KEY ... //#endif
const regex = /\\/\\/\\s*#ifdef\\s+(\\w+)\\s*([\\s\\S]*?)\\/\\/\\s*#endif/g;
return {
code: code.replace(regex, (match, key, content) => {
// 如果环境变量存在且为真,保留内容,否则删除
return env[key] ? content : '';
}),
map: null
};
}
};
}
使用:
// vite.config.js
import conditionalCompile from './plugins/conditional-compile-plugin.js';
export default {
plugins: [
conditionalCompile({
env: {
DEBUG: process.env.NODE_ENV === 'development',
PRO_FEATURE: false
}
})
]
};
源代码中:
function initApp() {
//#ifdef DEBUG
console.log('调试模式启动');
console.log('配置信息:', config);
//#endif
//#ifdef PRO_FEATURE
loadProModules();
//#endif
startApp();
}
构建后(DEBUG=true, PRO_FEATURE=false):
function initApp() {
console.log('调试模式启动');
console.log('配置信息:', config);
startApp();
}
五、进阶技巧
5.1 虚拟模块
创建不对应真实文件的模块:
// plugins/virtual-module-plugin.js
const virtualModuleId = 'virtual:app-config';
const resolvedVirtualModuleId = '\\0' + virtualModuleId;
export default function virtualModulePlugin() {
return {
name: 'virtual-module',
resolveId(id) {
if (id === virtualModuleId) {
return resolvedVirtualModuleId;
}
},
load(id) {
if (id === resolvedVirtualModuleId) {
return `
export const appName = 'My Awesome App';
export const version = '${process.env.npm_package_version}';
export const features = {
darkMode: true,
i18n: true
};
`;
}
}
};
}
使用:
import { appName, version, features } from 'virtual:app-config';
console.log(appName); // 'My Awesome App'
5.2 HMR 热更新支持
让插件支持热更新:
export default function hmrPlugin() {
return {
name: 'hmr-plugin',
handleHotUpdate({ server, modules, timestamp }) {
// 自定义 HMR 处理逻辑
// 过滤特定模块
const filtered = modules.filter(m => !m.id.includes('node_modules'));
console.log('🔄 热更新模块:', filtered.map(m => m.id));
// 返回模块列表,Vite 会继续处理
return filtered;
}
};
}
5.3 插件排序
控制插件执行顺序:
export default {
plugins: [
{
...myPlugin(),
enforce: 'pre' // 'pre' | 'post',默认 normal
}
]
};
六、发布你的插件
6.1 插件项目结构
vite-plugin-awesome/
├── src/
│ └── index.js # 插件入口
├── package.json
├── README.md
└── LICENSE
6.2 package.json 配置
{
"name": "vite-plugin-awesome",
"version": "1.0.0",
"description": "An awesome Vite plugin",
"main": "dist/index.js",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
"files": ["dist"],
"scripts": {
"build": "rollup -c",
"prepublishOnly": "npm run build"
},
"peerDependencies": {
"vite": "^4.0.0 || ^5.0.0"
},
"keywords": ["vite", "plugin", "vite-plugin"],
"license": "MIT"
}
6.3 发布到 npm
6.3 发布到 npm
npm login
npm publish
七、常用插件推荐
| 插件 | 功能 |
|---|---|
@vitejs/plugin-vue |
Vue 单文件组件支持 |
@vitejs/plugin-react |
React Fast Refresh |
vite-plugin-pwa |
PWA 支持 |
unplugin-auto-import |
自动导入 API |
vite-plugin-svg-icons |
SVG 图标雪碧图 |
vite-plugin-mock |
Mock 数据服务 |
八、总结
Vite 插件开发并不复杂,核心要点:
✅ 必须掌握:
- 插件基本结构(name + hooks)
- 常用钩子:
config、transform、configureServer - 虚拟模块的使用
✅ 进阶技能:
- HMR 热更新处理
- 开发服务器中间件
- 构建流程控制
✅ 最佳实践:
- 插件选项设计要灵活
- 提供详细的文档和示例
- 处理好错误边界
更多推荐



所有评论(0)