Vite 插件开发实战:打造属于你的构建工具

张开发
2026/4/18 13:48:33 15 分钟阅读

分享文章

Vite 插件开发实战:打造属于你的构建工具
一、为什么要学 Vite 插件开发在使用 Vite 的过程中你可能会遇到这些场景需要在构建时自动生成某些文件想要自定义模块解析逻辑需要在开发服务器中添加特定的 API 路由想要集成特定的代码检查或转换工具Vite 插件就是解决这些问题的钥匙Vite 插件 vs Rollup 插件Vite 的插件系统基于 Rollup但做了大量扩展特性Rollup 插件Vite 插件构建阶段✅ 支持✅ 支持开发服务器❌ 不支持✅ 支持HMR 热更新❌ 不支持✅ 支持配置解析❌ 不支持✅ 支持二、插件基础Hello WorldVite 插件本质上是一个对象或返回对象的函数包含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); });案例 2Mock 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(); }构建后DEBUGtrue, PRO_FEATUREfalsefunction 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 App5.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 └── LICENSE6.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 发布到 npm6.3 发布到 npmnpm login npm publish七、常用插件推荐插件功能vitejs/plugin-vueVue 单文件组件支持vitejs/plugin-reactReact Fast Refreshvite-plugin-pwaPWA 支持unplugin-auto-import自动导入 APIvite-plugin-svg-iconsSVG 图标雪碧图vite-plugin-mockMock 数据服务八、总结Vite 插件开发并不复杂核心要点✅必须掌握插件基本结构name hooks常用钩子config、transform、configureServer虚拟模块的使用✅进阶技能HMR 热更新处理开发服务器中间件构建流程控制✅最佳实践插件选项设计要灵活提供详细的文档和示例处理好错误边界

更多文章