🧩 一、什么是 Vite 插件?

Vite 插件本质上是基于 Rollup 插件 API 的扩展实现,但也提供了 专属的 Vite 插件钩子(vite 独有),用于处理 dev server、HMR、HTML 注入等功能。

Vite 插件 = Rollup 插件 + Vite 扩展钩子

🧬 二、Vite 插件生命周期概览

生命周期大致分为两大阶段:

阶段描述
开发阶段处理 dev server 启动、HMR、转码等
构建阶段调用 Rollup 构建流程,产物打包

完整钩子图:

┌──────────── dev server 启动

├── config
├── configResolved
├── configureServer

├── transformIndexHtml
├── resolveId
├── load
├── transform

├── handleHotUpdate

├──────────── build 时

├── options
├── buildStart
├── resolveId
├── load
├── transform
├── buildEnd
├── generateBundle
└── closeBundle

🔧 三、关键生命周期钩子详解

1️⃣ config / configResolved

  • config:插件最早执行阶段,用于动态修改 Vite 配置。
  • configResolved:Vite 配置对象被完整解析后的 hook,适合读取最终配置。
export function myPlugin(): Plugin {
  return {
    name: 'my-plugin',
    config(config, { command }) {
      console.log('vite config:', config)
    },
    configResolved(resolvedConfig) {
      console.log('final config:', resolvedConfig)
    }
  }
}

2️⃣ configureServer

  • 仅在开发环境生效
  • 注入中间件、定制 dev server 行为(例如:mock 数据)
configureServer(server) {
  server.middlewares.use((req, res, next) => {
    if (req.url === '/api/mock') {
      res.end(JSON.stringify({ msg: 'hello' }))
    } else {
      next()
    }
  })
}

3️⃣ transformIndexHtml

  • 修改入口 HTML 内容(如注入 CDN、meta 标签等)
transformIndexHtml(html) {
  return html.replace(
    '</head>',
    `<script src="https://xx.cdn.com/abc.js"></script></head>`
  )
}

4️⃣ resolveId / load / transform

这三个钩子贯穿开发与构建阶段,是处理文件加载的核心。

钩子作用
resolveId解析模块路径(虚拟模块)
load返回模块内容
transform对源码进行转换
resolveId(id) {
  if (id === 'virtual:env') return '\0virtual-env'
},
load(id) {
  if (id === '\0virtual-env') {
    return `export const env = "dev"`
  }
}

5️⃣ handleHotUpdate

  • 用于处理 HMR(热更新逻辑)
  • 监听文件变化后自定义响应行为
handleHotUpdate({ file, server }) {
  if (file.endsWith('.json')) {
    server.ws.send({
      type: 'custom',
      event: 'json-update',
      data: { file }
    })
  }
}

6️⃣ generateBundle / closeBundle

  • 构建阶段生成产物前后执行
  • 可用于分析构建产物 / 修改 bundle 输出
generateBundle(options, bundle) {
  console.log('Bundle keys:', Object.keys(bundle))
},
closeBundle() {
  console.log('🎉 构建结束')
}

🧪 四、完整插件示例(虚拟模块 + dev mock + 构建日志)

// vite-plugin-demo.ts
export default function demoPlugin(): Plugin {
  return {
    name: 'vite-plugin-demo',
    resolveId(id) {
      if (id === 'virtual:greeting') return '\0greeting'
    },
    load(id) {
      if (id === '\0greeting') {
        return `export default 'Hello from virtual module'`
      }
    },
    configureServer(server) {
      server.middlewares.use((req, res, next) => {
        if (req.url === '/api/hello') {
          res.end(JSON.stringify({ msg: 'Hello from mock' }))
        } else {
          next()
        }
      })
    },
    transformIndexHtml(html) {
      return html.replace('</head>', '<meta name="demo" /><script>alert("demo")</script></head>')
    },
    generateBundle(_, bundle) {
      console.log('👀 打包生成的文件有:', Object.keys(bundle))
    },
    closeBundle() {
      console.log('✅ 构建完成')
    }
  }
}

❓问答卡片

  • Vite 插件和 Rollup 插件的关系?
  • 插件生命周期中 dev 特有钩子有哪些?
  • transformIndexHtml 有哪些应用场景?
  • 如何通过插件实现虚拟模块?
  • 如何通过插件实现 mock 接口?有哪些优缺点?