乐于分享
好东西不私藏

Vite 插件体系深度:手写一个生产级插件,搞懂钩子执行顺序

Vite 插件体系深度:手写一个生产级插件,搞懂钩子执行顺序

别再用插件了,自己写一个才知道 Vite 有多强

你是不是也这样用 Vite?

插件配好了,项目跑起来了,然后就没了。

但你有没有想过:这些插件是怎么工作的?Vite 内部是怎么调度它们的?

今天,带你去 Vite 源码里转一圈。读完这篇,你将学会:

  • Vite 插件体系的设计原理

  • 所有核心钩子及其执行顺序

  • 手写一个生产级虚拟模块插件

一、Vite 插件体系:站在 Rollup 的肩膀上

Vite 的插件机制,本质上是Rollup 插件 + Vite 特有插件 的组合。

为什么是 Rollup?

因为 Vite 的生产构建用的就是 Rollup。为了复用 Rollup 庞大的插件生态,Vite 直接兼容了 Rollup 的插件接口。你在 Vite 里能用rollup-plugin-xxx,就是这个原因。

 Vite 不只是 Rollup 的搬运工。它在 Rollup 的基础上,加入了Vite 独有的钩子,专门处理开发服务器的场景。

二、钩子分两类:搞懂执行阶段

Vite 插件的钩子可以分为两大阵营:

阵营

执行环境

代表钩子

作用

Build钩子

开发+构建

resolveIdloadtransform

模块解析、加载、转换

Output钩子

仅构建

generateBundlewriteBundle

打包产物生成、输出

Vite独有钩子

开发服务器

configureServertransformIndexHtml

中间件注入、HTML转换

注意moduleParsed钩子在开发环境不会被调用,Vite 为了性能会避免完整 AST 解析。

三、执行顺序全解析:一张图看懂

当你在vite.config.ts 里配置一堆插件,Vite 是这样调度:

3.1 配置阶段

  1. config钩子:修改配置,所有插件依次执行

  2. configResolved 钩子:配置解析完成,可以读取最终配置

3.2 开发服务器阶段

options钩子Rollup 兼容)

buildStart钩子:构建开始

对每个模块请求:

  • resolveId:解析模块路径(返回null 交给下一个插件,返回string 则终止

  • load:加载模块内容

  • transform:转换源代码

buildEnd钩子:构建结束

closeBundle钩子:服务器关闭

3.3 Vite 独有钩子

四、手写一个生产级插件:虚拟模块

虚拟模块是 Vite 插件中最实用的模式之一。它让你可以动态生成"不存在"的模块,代码直接 import 进来。

Step 1:定义插件结构

Step 2:使用插件

五、插件执行顺序:enforce apply

5.1 enforce:控制执行时机

Vite 插件的执行顺序由 enforce 属性控制:

执行顺序:pre 插件→ Vite 核心插件 → 普通插件 → post 插件

5.2 apply:控制执行环境

六、调试神器:vite-plugin-inspect

写插件时,最难的是调试。不知道哪个插件改了代码,不知道钩子执行顺序。

Vite 官方推荐vite-plugin-inspect

启动后访问localhost:5173/__inspect/,你可以看到:

  • 每个模块被哪些插件处理过

  • 每个钩子的执行结果

  • 完整的转换链路

最后

Vite 插件体系的核心就三句话:

  1. 兼容 Rollup:复用生态,resolveIdloadtransform 三件套

  2. 扩展 ViteconfigureServertransformIndexHtml 解决开发场景

  3. 顺序控制enforce定前后,apply 定环境

掌握了这三句话,你就能从"用插件的人"变成"写插件的人"。

下次遇到重复性的构建需求,别复制粘贴了。写个插件,一劳永逸。

如果这篇文章对你有帮助,欢迎点赞、在看、转发。