插件的基本结构与生命周期
插件是 Vite 生态的核心扩展机制,通过钩子函数介入构建流程。理解插件的结构和生命周期对定制化开发至关重要,它决定了代码何时执行以及如何影响构建结果。
插件的基本结构
一个标准的 Vite 插件需要导出包含特定属性的对象。最基本的形态如下:
// my-plugin.js
export default function myPlugin(options) {
return {
name: 'vite-plugin-my', // 必填的唯一标识
config(config) { /*...*/ }, // 配置钩子
transform(code, id) { /*...*/ } // 转换钩子
}
}
关键结构要素包括:
- name:插件名称,建议以
vite-plugin-
前缀命名 - enforce:调整执行顺序(pre|post)
- apply:指定插件在开发/生产环境的应用场景
- config:修改 Vite 配置的入口
- transform:文件内容转换处理器
复杂插件可能包含多个生命周期钩子:
export default {
name: 'vite-plugin-multi-hook',
// 配置阶段
config(config, env) {
console.log('当前模式:', env.mode)
},
// 构建阶段
buildStart() {
console.log('构建开始')
},
// 文件处理
load(id) {
if (id.endsWith('.custom')) {
return 'export default "Hacked!"'
}
}
}
生命周期阶段详解
配置解析阶段
config
钩子在解析用户配置前执行,支持同步/异步操作:
{
async config(config, env) {
if (env.mode === 'development') {
return {
server: {
port: 3001
}
}
}
}
}
configResolved
在配置确定后触发,适合读取最终配置:
{
configResolved(resolvedConfig) {
console.log('最终端口号:', resolvedConfig.server.port)
}
}
模块加载阶段
resolveId
控制模块路径解析:
{
resolveId(source, importer) {
if (source === 'magic') {
return '\0virtual:magic' // 添加虚拟模块标记
}
}
}
load
处理模块内容加载:
{
load(id) {
if (id === '\0virtual:magic') {
return 'export const spell = "Abracadabra"'
}
}
}
转换处理阶段
transform
对源代码进行转换:
{
transform(code, id) {
if (id.endsWith('.vue')) {
return code.replace('<template>', '<template lang="pug">')
}
}
}
transformIndexHtml
专门处理 HTML 入口:
{
transformIndexHtml(html) {
return html.replace(
'<head>',
'<head><meta name="injected" content="true">'
)
}
}
构建事件阶段
buildStart
和 buildEnd
标记构建边界:
{
buildStart() {
this.startTime = Date.now()
},
buildEnd() {
console.log(`构建耗时: ${Date.now() - this.startTime}ms`)
}
}
closeBundle
在打包完成后执行清理操作:
{
async closeBundle() {
await fs.remove('temp/')
}
}
钩子执行顺序
完整生命周期流程示例:
config
→ 修改初始配置configResolved
→ 读取最终配置configureServer
→ 开发服务器配置buildStart
→ 构建开始resolveId
→ 解析模块路径load
→ 加载模块内容transform
→ 转换模块内容buildEnd
→ 构建结束closeBundle
→ 资源清理
通过enforce
调整执行位置:
{
name: 'early-plugin',
enforce: 'pre', // 在普通插件前执行
transform(code) { /*...*/ }
}
{
name: 'late-plugin',
enforce: 'post', // 在普通插件后执行
transform(code) { /*...*/ }
}
实际应用示例
实现一个自动注入环境变量的插件:
// vite-plugin-inject-env.js
export default function injectEnv() {
return {
name: 'vite-plugin-inject-env',
transform(code, id) {
if (!id.includes('node_modules')) {
return code.replace(
/import\.meta\.env/g,
'JSON.parse(process.env.VITE_ENV)'
)
}
},
generateBundle(options, bundle) {
for (const file in bundle) {
if (file.endsWith('.js')) {
bundle[file].code = `const process = { env: { VITE_ENV: ${JSON.stringify(process.env)} } };\n${bundle[file].code}`
}
}
}
}
}
CSS 预处理插件示例:
// vite-plugin-less-vars.js
import less from 'less'
export default function lessVars(vars) {
return {
name: 'vite-plugin-less-vars',
async transform(code, id) {
if (!id.endsWith('.less')) return
const result = await less.render(code, {
globalVars: vars,
javascriptEnabled: true
})
return {
code: result.css,
map: result.map
}
}
}
}
插件开发注意事项
- 上下文保持:使用
this
访问插件上下文
{
configureServer(server) {
this.server = server // 存储引用
}
}
- 热更新处理:通过
handleHotUpdate
实现
{
handleHotUpdate(ctx) {
if (ctx.file.endsWith('.data')) {
ctx.server.ws.send({ type: 'full-reload' })
return []
}
}
}
- 虚拟文件系统:结合
resolveId
和load
{
resolveId(id) {
if (id.startsWith('virtual:')) {
return '\0' + id
}
},
load(id) {
if (id.startsWith('\0virtual:')) {
return `export default ${JSON.stringify({ magic: 42 })}`
}
}
}
- 性能优化:使用
moduleParsed
跟踪依赖
{
moduleParsed(moduleInfo) {
if (moduleInfo.id.includes('utils')) {
console.log('发现工具模块依赖:', moduleInfo.importedIds)
}
}
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:配置智能提示与类型支持
下一篇:官方核心插件功能介绍