前言
Vite 之所以能颠覆 Webpack 的统治地位,不仅是因为它在开发阶段的"快",更在于它巧妙地结合了 原生 ESM 与 Rollup 的生产构建能力。本文将带你拆解 Vite 打包的每一个步骤,并揭秘其插件系统的核心钩子。
一、 Vite 生产打包流水线
Vite 的生产构建完全基于 Rollup 实现,但在 Rollup 打包前后增加了 Vite 特有的预处理和后优化步骤,整个流程可以分为以下 6 个核心阶段:
步骤 1:加载并解析配置
Vite 会优先读取项目根目录的vite.config.js/ts(或vite.config.mjs)配置文件,同时合并命令行参数和默认配置,形成最终的运行配置。
- 解析核心配置项:
root(项目根目录)、base(公共基础路径)、build(打包配置,如输出目录、目标环境、代码分割等)、plugins(插件)、resolve(路径解析)等 - 同时会读取
package.json中的type: module等配置,确定项目的模块规范
配置合并优先级为:命令行参数 > 配置文件导出的配置 > Vite 内置默认配置
步骤 2:预构建与依赖分析
这是 Vite 区别于传统打包工具的关键步骤,主要为了解决第三方依赖的兼容性和打包性能问题。
- Vite 会扫描项目中的所有依赖(主要是
node_modules中的第三方包),对非 ES 模块的依赖(如 CommonJS、UMD 格式)进行预构建,统一转换为标准 ES 模块 - 分析项目入口文件(默认是根目录的
index.html),递归解析所有文件的依赖关系(包括.vue/.js/.ts/.css/.scss等各种类型的文件),构建完整的模块依赖图
补充 :预构建的结果会被缓存到
node_modules/.vite目录,只有当依赖发生变化时才会重新预构建,极大提升了后续打包速度
步骤 3:插件执行
Vite 会按照配置的顺序依次执行所有插件,并调用相应的插件生命周期钩子,在这个阶段完成各种文件转换、代码预处理和资源处理操作。
-
插件会在不同的生命周期钩子中执行对应的逻辑,例如:
- 对
.vue/.jsx/.tsx等非原生 JS 文件进行编译转换 - 小图片 / 字体等静态资源的 base64 转换(由 Vite 内置的 assets 插件处理)
- CSS 预处理器编译、PostCSS 处理、CSS Modules 转换等
- 对
步骤 4:使用 Rollup 进行核心打包
这是生产构建的核心阶段,Vite 将完全委托给 Rollup 进行代码打包和优化。
- 按入口文件拆分代码块(chunk),同时支持根据动态导入
import()自动拆分代码块,还可以通过配置将第三方依赖单独拆分为 vendor chunk - 生成兼容目标环境的代码:默认打包为现代浏览器支持的 ES 模块格式,也可通过
build.target配置兼容 ES5 及更低版本 - 处理模块间的依赖引用:将代码中的相对路径替换为配置的
base路径,确保生产环境下所有资源都能正确加载 - 执行 Rollup 特有的优化:包括 Tree Shaking 移除无用代码、作用域提升(Scope Hoisting)减少代码体积等
步骤 5:Rollup 产物最终优化
在 Rollup 完成基础打包后,Vite 会对输出的产物进行最后一轮的生产环境优化。
- 对 JavaScript 和 CSS 代码进行压缩混淆(默认使用 Terser 压缩 JS,esbuild 压缩 CSS)
- 生成资源哈希文件名,实现静态资源的长效缓存
- 可选生成 sourcemap 文件,方便生产环境调试
- 可选生成
manifest.json文件,记录资源文件名与哈希后的文件名的映射关系,用于后端集成
步骤 6:输出最终产物
将所有打包和优化后的产物输出到指定的目录(默认是dist目录)。
- 静态资源(JS、CSS、图片、字体等)会输出到
dist/assets目录 - 入口 HTML 文件会输出到
dist根目录 - 其他配置的静态资源会按照原目录结构输出到
dist目录
二、 揭秘 Vite 插件钩子 (Hooks)
Vite 的插件系统扩展自 Rollup 的插件系统,同时增加了一些 Vite 特有的钩子函数,以支持开发服务器和热更新等 Vite 独有的功能。
2.1 通用 Rollup 兼容钩子
Vite 在开发阶段会模拟 Rollup 的行为,调用一系列与 Rollup 兼容的钩子;而在生产环境下,由于 Vite 直接使用 Rollup 进行打包,所有 Rollup 插件钩子都会生效。
这些通用钩子主要分为三个阶段:
- 服务器启动阶段 :
options和buildStart钩子会在开发服务启动时被调用 - 请求响应阶段 :当浏览器发起请求时,Vite 内部依次调用
resolveId、load和transform钩子 - 服务器关闭阶段 :Vite 会依次执行
buildEnd和closeBundle钩子
重要说明 :除了以上钩子,其他 Rollup 插件钩子(如moduleParsed、renderChunk等)均不会在 Vite 开发阶段调用。这是因为开发阶段 Vite 采用按需编译的模式,不需要对整个项目进行完整打包。
2.2 Vite 独有钩子
Vite 提供了一些独有的钩子函数,这些钩子只会在 Vite 内部调用,放到纯 Rollup 环境中会被直接忽略。
| 钩子名称 | 调用时机 | 主要用途 |
|---|---|---|
config |
Vite 读取完配置文件之后,最终配置合并之前 | 对用户导出的配置对象进行自定义修改,例如注入默认值、根据环境动态调整配置、合并插件配置等 |
configResolved |
Vite 完成最终配置解析之后 | 此时配置已完全合并且不可再修改,常用于获取最终配置进行调试,或根据最终配置初始化插件内部状态 |
configureServer |
仅在开发服务器启动时调用 | 扩展或拦截开发服务器行为,例如添加自定义中间件、模拟 API 接口、修改服务器配置、实现请求代理等 |
transformIndexHtml |
转换原始 HTML 文件内容时调用 | 动态修改 index.html 内容,例如注入脚本标签、修改 meta 标签、添加全局变量、SSR 内容注入等 |
handleHotUpdate |
Vite 服务端处理热更新时调用 | 自定义热更新逻辑,例如过滤不需要热更新的模块、触发自定义的热更新行为、向客户端发送自定义消息等 |
三、 核心:钩子函数的执行顺序
理解执行顺序是编写高质量插件的前提。
1. 服务启动阶段
config → configResolved → options → configureServer → buildStart
2. 请求响应阶段
- HTML 请求 :仅执行
transformIndexHtml。 - 非 HTML 请求 (JS/CSS等):
resolveId→load→transform。
3. 热更新与关闭阶段
- HMR 触发 :
handleHotUpdate。 - 服务关闭 :
buildEnd→closeBundle。

四、 知识扩展:开发与生产的差异
- 开发阶段 :Vite 利用浏览器原生 ESM,只有在文件被请求时才触发
transform钩子,这是其"快"的底层逻辑。 - 生产阶段:Vite 完完全全变成了一个"Rollup 配置封装器",所有插件钩子都会遵循 Rollup 的打包逻辑执行。