插件机制:Rollup 插件接口 + Vite 专属钩子
本节目标
- 理解「Vite 插件本质是一个对象(兼容 Rollup 插件)」
- 知道 Vite 在 serve / build 两阶段分别用哪些钩子
- 认识几个 Vite 专属钩子(transform / config / transformIndexHtml)
Vite 的插件系统直接复用 Rollup 的插件接口——一个插件就是「一个带 name 和若干钩子方法的对象」。这让你写的插件在「开发服务器」和「生产构建」里都能工作(构建阶段走 Rollup)。
// 一个最小 Vite 插件骨架
export default function myPlugin() {
return {
name: 'my-plugin', // 插件名(必填,Rollup 要求唯一)
// —— Rollup 通用钩子 ——
transform(code, id) {
// code: 文件源码;id: 文件绝对路径
// 返回修改后的代码(或不返回表示不动)
if (id.endsWith('.txt')) {
return `export default ${JSON.stringify(code)}`
}
},
// —— Vite 专属钩子(serve 阶段)——
configureServer(server) {
// 在开发服务器上加自定义中间件
server.middlewares.use((req, res, next) => {
if (req.url === '/ping') { res.end('pong'); return }
next()
})
},
// —— Vite 专属钩子:改 index.html ——
transformIndexHtml(html) {
return html.replace('<title>', '<title>[DEV] ')
},
}
}关键心智点:
- serve 阶段(开发):Vite 用自己的中间件管线,插件可用
configureServer、transform、transformIndexHtml等钩子干预请求与响应。 - build 阶段(生产):Vite 调用 Rollup,插件用 Rollup 标准钩子(
resolveId、load、transform、generateBundle等)工作。 - 同一个
transform钩子,在两边都有效——这正是「一套插件通吃开发和生产」的原因。
// vite.config.js 里挂载插件
import { defineConfig } from 'vite'
import myPlugin from './my-plugin.js'
export default defineConfig({
plugins: [myPlugin()],
})名词解释
Rollup 插件接口:Vite 插件遵循 Rollup 的插件规范(一个含 name + 钩子的对象)。这意味着大量 Rollup 生态插件可直接用于 Vite。
transform 钩子:最常用的钩子,接收「文件源码 + 路径」,返回「修改后的代码」。可用于改写、注入、转译任意模块。
configureServer:Vite 专属钩子,只在开发服务器里执行,用来挂自定义中间件(如 mock 接口、特殊路由)。
transformIndexHtml:Vite 专属钩子,用来在开发/构建时改写 index.html(注入脚本、改 title 等)。
课后练习
练习 1:为什么说「Vite 插件能复用 Rollup 生态」?
答案:因为 Vite 插件接口直接兼容 Rollup 插件规范。生产构建时 Vite 底层就是 Rollup,所以那些为 Rollup 写的插件(只要不涉及 Vite 专属钩子之外的冲突)大多能直接在 Vite 里用。
练习 2:configureServer 钩子会在生产构建时执行吗?
答案:不会。它是 Vite 专属的「开发服务器」钩子,只在
vite dev(serve 阶段)触发;生产构建走 Rollup,不会启动 dev server,所以该钩子不执行。
总结
Vite 插件的灵魂是**「一个对象 = Rollup 插件 + Vite 专属钩子」**,serve 阶段用 configureServer/transformIndexHtml 干预服务器,build 阶段用 Rollup 钩子(transform/load…)参与打包,而 transform 在两阶段通用。理解了这套「双阶段钩子」模型,写插件就只是「选对钩子」。下一节我们亲手写一个:虚拟模块 + 注入环境变量。