引言
vite build 背后是 Rollup——一个以「插件钩子」组织的打包器:从解析入口到生成产物,每个阶段都有钩子让插件介入。理解这条管线,你才知道产物为什么长这样、chunk 是怎么分的、manifest 是什么、插件该挂哪个钩子。本文讲透 Rollup 构建管线:先讲从入口到产物的完整链路(输入→模块图→chunk→产物),再讲 Rollup 插件钩子体系(build 阶段钩子 resolveId/load/transform、模块图钩子、输出阶段钩子)、解析与加载阶段(resolveId/load 的职责)、转换阶段(transform 与代码处理)、打包与代码生成阶段(图→chunk 的算法、renderChunk/generateBundle)、产物结构(chunk/asset/entry 的关系与命名)、manifest 与运行时注入(build.manifest、modulepreload)、浏览器目标与降级(build.target、legacy 插件)、最后是构建诊断(debug 日志与故障排查)。
前置:/vite-build-optimization/(构建配置与优化)、/vite-import-graph-internals/(导入图)、/vite-plugin-development/(插件开发)。
目录
- 1. 从入口到产物:构建全链路
- 2. Rollup 的插件钩子
- 3. 解析与加载阶段
- 4. 转换与 transform 阶段
- 5. 打包与代码生成阶段
- 6. 产物结构:chunk、asset、entry
- 7. manifest 与运行时注入
- 8. 浏览器目标与降级
- 9. 构建诊断:debug 与日志
- 10. 速查表与一句话记忆
- 延伸阅读
1. 从入口到产物:构建全链路
一次 vite build 的完整链路:
1. 输入:HTML 入口(index.html → script 入口)
2. 解析入口:resolveId → 定位入口模块
3. 加载模块:load → 读源码
4. 转换模块:transform → 转译(TS/JSX/CSS)
5. 扫描依赖:es-module-lexer → 收集 import
6. 递归加载:展开所有可达模块(全量导入图)
7. 摇树:标记使用 + 删除未用
8. 分 chunk:按动态导入/公共依赖分割
9. 代码生成:生成 chunk/asset 文件
10. 输出:写入 dist + 生成 manifest
→ 构建 = 输入 → 模块图 → 摇树分割 → 生成输出
两个阶段(build 与 generate):
Rollup 分两阶段:
Build 阶段:解析/加载/转换 → 得到「模块图」
Generate 阶段:图 → chunk → 生成产物内容
插件钩子也分两阶段:
build 钩子(resolveId/load/transform/...)
output 钩子(renderChunk/generateBundle/...)
→ 先「建图」再「出产」,钩子按阶段分
Vite 在 Rollup 之上的职责:
- Vite 把 Rollup 作为「构建内核」
- Vite 负责:HTML 入口处理、CSS 处理、assets、env 替换
- Rollup 负责:模块图、摇树、chunk 分割、代码生成
- 两者组合 = Vite build
→ 分工:Vite 管「前端工程」,Rollup 管「打包内核」
构建 vs dev 的差异:
- dev:逐模块转换(懒)+ esbuild 转译(快)
- build:全量模块图 + Rollup 打包(优化产物)
- dev 无 chunk 分割(按需请求)
- build 有摇树 + 分割 + 压缩(产物优化)
→ dev 服务开发、build 优化产物,两套路径
构建的可配置性:
// vite.config.ts
export default defineConfig({
build: {
outDir: 'dist',
rollupOptions: {
input: 'src/main.ts', // 入口
output: { format: 'es' }, // 输出格式
plugins: [myPlugin()] // 注入插件
}
}
})
// rollupOptions = 直接透传给 Rollup
心智:一次构建链路 = 输入 → resolveId → load → transform → 扫依赖 → 递归建全量图 → 摇树 → 分割 chunk → 代码生成 → 输出 + manifest;分 build(建图)与 generate(出产)两阶段,钩子也按阶段分;Vite 管前端工程(HTML/CSS/assets/env),Rollup 管打包内核(图/摇树/分割/生成);dev 是懒转换、build 是优化产物;rollupOptions 直接透传给 Rollup。
2. Rollup 的插件钩子
钩子的两类(Build / Output):
Build 阶段钩子(建图):
- resolveId:把导入解析为 id
- load:读模块源码
- transform:转换代码
- moduleParsed:模块解析后(查看依赖)
- buildEnd:图构建完成
Output 阶段钩子(出产):
- renderChunk:生成 chunk 前改代码
- generateBundle:写文件前改产物
- writeBundle:写文件后
→ 钩子 = 插件在各阶段「插入逻辑」的口子
常用钩子一览:
resolveId(source, importer, options) → id | null
- 解析导入;返回 null 走默认
load(id) → 代码 | null
- 读取模块内容;null 走默认
transform(code, id) → code | null
- 转换代码;返回转换后代码
renderChunk(code, chunk, options) → code | null
- 生成 chunk 内容时改写(minify 前)
generateBundle(outputOptions, bundle) → void
- 产物 bundle 对象:可改/增/删文件
钩子的执行顺序:
resolveId → load → transform → moduleParsed
(每个模块一轮,递归所有模块)
→ buildEnd → renderChunk(每个 chunk)
→ generateBundle → writeBundle
→ 顺序 = 建图先、出产后
钩子的返回约定:
- 返回 null/undefined:交给下一个插件/默认处理
- 返回对象(id):resolveId 可返回 { id, external }
- 返回字符串:load 返回源码、transform 返回新代码
- 抛出错误:终止构建(可捕获提示)
→ 约定:不拦截就返回 null,拦截就返回结果
Vite 对 Rollup 钩子的扩展:
- Vite 特有钩子:config/configResolved(配置阶段)
- Vite 特有钩子:configureServer/handleHotUpdate(dev)
- Vite 会把 Vite 插件转成 Rollup 插件(映射钩子)
- Rollup 钩子在 Vite 插件里同样可用(transform 等)
→ Vite 插件 = Rollup 钩子 + Vite 特有钩子
写钩子的注意:
- 钩子名要准确(拼错不报错但无效)
- async 钩子返回 Promise(慢但可 await)
- 缓存:同一模块只跑一次(除非失效)
- 顺序依赖:先 run 的钩子优先级(插件顺序)
→ 钩子 = 阶段 + 顺序 + 返回约定的三重约束
心智:Rollup 钩子分 build(resolveId/load/transform/moduleParsed/buildEnd 建图)与 output(renderChunk/generateBundle/writeBundle 出产)两阶段,执行顺序建图先出产后;返回约定:不拦截返回 null、拦截返回结果、抛错终止;Vite 插件 = Rollup 钩子 + Vite 特有钩子(config/configureServer/handleHotUpdate);注意钩子名准确、async、缓存、插件顺序。
3. 解析与加载阶段
resolveId:从导入到 id:
// 解析插件
export default {
name: 'resolve-example',
resolveId(source, importer, options) {
if (source === 'virtual:config') {
// 返回 id:虚拟模块用 \0 前缀标记
return '\0virtual:config';
}
// 其他导入走默认解析(node_modules 等)
return null;
}
}
load:从 id 到源码:
// 加载插件(提供虚拟模块内容)
export default {
name: 'load-example',
load(id) {
if (id === '\0virtual:config') {
return `export const config = { theme: 'dark' }`;
}
return null; // 其他模块走默认读文件
}
}
解析与加载的职责划分:
- resolveId:回答「这个导入指向哪个模块」→ id
- load:回答「这个 id 的内容是什么」→ 源码
- 解析到 id 但没加载:走默认文件读取
- 虚拟模块:resolve 到 \0 前缀 id + load 返回内容
→ resolve 定位、load 取内容,两步分开
非文件模块(虚拟模块):
- \0 前缀:标记「非真实文件」的模块(隐藏于文件系统逻辑)
- 常见:virtual:config、virtual:svg、virtual:md
- 好处:打包器不会去磁盘找(省 IO 报错)
- 约定:真实文件不加 \0,虚拟必须加
→ 虚拟模块 = resolve 到 \0 id + load 给内容
解析的缓存与顺序:
- 多插件 resolveId 按顺序尝试(先返回先赢)
- 找不到 → 默认解析(node_modules/相对路径)
- 解析结果缓存(同一导入不再重复解析)
- 依赖更新(装新包)→ 缓存失效(需重启 dev/重建)
→ 解析 = 顺序尝试 + 默认兜底 + 缓存
加载阶段的注意:
- load 返回 null 时走文件系统(默认)
- 二进制/大文件:load 可用(读成字符串/ArrayBuffer)
- 目录导入:load 可能被触发两次(index 解析)
- 加载错误:抛错会定位到模块
→ 加载 = 默认读文件 + 虚拟模块拦截 + 错误定位
心智:解析加载阶段 = resolveId(导入→id,虚拟模块返回 \0 前缀 id)+ load(id→源码,\0 模块返回内容);职责:resolve 定位、load 取内容;\0 前缀标记非文件模块(打包器不去磁盘找);多插件顺序尝试 + 默认兜底(node_modules/相对路径)+ 缓存;加载默认读文件、可拦截虚拟、错误定位到模块。
4. 转换与 transform 阶段
transform:改代码的钩子:
// 转换插件:把 TODO 注释替换
export default {
name: 'replace-todo',
transform(code, id) {
if (id.includes('/src/')) {
return code.replace(/TODO/g, 'TODO(app)');
}
return null;
}
}
转换的常见用途:
- 转译:TS → JS、JSX → JS、Vue SFC → JS
- 替换:环境变量、alias、常量(define)
- 注入:代码替换/插桩(埋点、HMR 注入)
- 预编译:CSS 预处理、模板编译
- 分析:解析模块、收集元数据(不修改也可返回 null)
→ 转换 = 每个模块「进入产物前」的处理
转换的输入输出:
输入:源码(字符串)+ id(模块标识)
输出:转换后代码(字符串)| null(不改)
附加:可返回 sourcemap、可声明非 JS 处理
→ 转换 = code in → code out,可选 sourcemap
转换链(多插件顺序):
源码 → 插件A transform → 插件B transform → ... → 最终代码
- 顺序 = 插件注册顺序
- 前一个的输出 = 后一个的输入
- 每个都可返回 null(透传)
- 转换后再「重新扫描依赖」(可能改变)
→ 转换链 = 顺序管道 + 可选透传 + 依赖重扫
转换的性能:
- 每个模块跑所有插件的 transform(耗时)
- dev:转换懒执行(用到才转)+ 缓存
- build:全量转换一次
- 大项目:transform 是构建耗时大头
→ 性能 = 缓存 + 懒执行 + 减少不必要转换
转换的调试:
- vite-plugin-inspect:查看每个模块「转换前/后」代码
- transform 里 console 打日志(id + 耗时)
- 断点:插件在 IDE 里调试
- 对比 dev/build 的转换(路径可能不同)
→ 调试 = inspect + 日志 + 断点
心智:transform = 每个模块进入产物前的处理钩子(code+id 进、code/sourcemap 出、null 透传);用途:转译(TS/JSX/Vue)、替换(env/define)、注入(埋点/HMR)、预编译;转换链 = 多插件顺序管道 + 前输出后输入 + 转换后重扫依赖;性能:dev 懒执行 + 缓存、build 全量一次,transform 是构建耗时大头;调试用 vite-plugin-inspect 看转换前/后。
5. 打包与代码生成阶段
从模块图到 chunk:
- 图构建完:所有可达模块在「模块图」里
- 摇树后:只保留被使用模块/导出
- 分 chunk:把模块分配到「输出 chunk」
- 入口模块 → 入口 chunk
- 动态导入模块 → 独立 chunk(懒加载)
- 公共依赖 → 共享 chunk(被多入口引用)
- 生成:每个 chunk 生成「打包后代码」
→ 打包 = 图 → 摇树 → chunk 分配 → 代码生成
chunk 分配算法:
- 按「谁加载谁」组织
- 动态导入点 = 天然分割点
- 公共依赖提升到父 chunk 或独立共享 chunk
- manualChunks 手动控制(按自定义规则)
- 目标:首包最小 + 缓存友好
→ 分配 = 分割点 + 公共提取 + 手动覆盖
renderChunk:生成时改写:
// 在生成 chunk 内容时改写(minify 前)
export default {
name: 'render-chunk-example',
renderChunk(code, chunk, options) {
// chunk 含模块信息,code 是生成内容
return code.replace(/console\.log/g, 'console.info');
}
}
generateBundle:改产物文件集:
// 在写文件前增删产物
export default {
name: 'generate-bundle-example',
generateBundle(outputOptions, bundle) {
// 删除 sourcemap
for (const [name, item] of Object.entries(bundle)) {
if (name.endsWith('.map')) delete bundle[name];
}
}
}
代码生成的关键选项:
- format:es/cjs/umd/iife(产物格式)
- minify:压缩(esbuild 默认)
- sourcemap:是否生成 map
- chunkFileNames/assetFileNames:命名规则
- generatedCode:ES 特性降级级别
→ 生成选项 = 格式 + 压缩 + map + 命名
生成阶段的诊断:
- renderChunk 里看 chunk 内容(拼接是否正确)
- generateBundle 看产物文件清单(是否有预期文件)
- 产物大小:visualizer(占比分析)
- 重复模块:同依赖出现在多个 chunk(要提取)
→ 诊断 = chunk 内容 + 文件清单 + 大小分布
心智:打包生成阶段 = 图 → 摇树 → chunk 分配(入口 chunk + 动态独立 + 公共提取 + manualChunks)→ 代码生成(renderChunk 改写、generateBundle 改文件集);分配目标首包最小 + 缓存友好;生成选项:format/minify/sourcemap/命名/generatedCode;诊断:renderChunk 看内容、generateBundle 看清单、visualizer 看占比、查重复模块。
6. 产物结构:chunk、asset、entry
产物三类文件:
chunk:JS 模块打包后的文件(.js)
- 入口 chunk:由入口模块生成(含启动代码)
- 动态 chunk:懒加载的独立文件
- 公共 chunk:多入口共享的依赖
asset:非 JS 资源(CSS/图片/字体/...)
- 独立文件 + 内容哈希命名
entry:HTML 入口(Vite 生成)
- 引用入口 chunk 与关键 asset
→ 产物 = chunk(JS)+ asset(资源)+ entry(HTML)
chunk 的关系:
- 入口 chunk 引用动态 chunk(import() 运行时加载)
- 公共 chunk 被多个入口 chunk 引用(import 共享)
- 依赖关系由「import 语句」表达(ESM 产物)
- modulepreload 预加载(HTML 注入)
→ chunk = 有依赖关系的「ESM 文件集合」
命名规则:
- chunk 默认:[name]-[hash].js(hash = 内容哈希)
- 入口 chunk:[name] = 入口名(如 index-[hash].js)
- 动态 chunk:按导入点命名(About-[hash].js)
- asset:[name]-[hash][ext](内容哈希防缓存失效)
- 手动配置:chunkFileNames/assetFileNames
→ 命名 = 可读名 + 内容哈希(缓存友好)
CSS 的处理:
- CSS 独立成 asset(或注入 JS)
- CSS 与 chunk 关联(该 chunk 导入的 CSS)
- 生产常用:独立 CSS 文件 + link 引入
- CSS 也按内容哈希命名
→ CSS = 与 chunk 关联的 asset,独立缓存
静态资源(assets):
- import logo from './logo.png' → asset 文件
- 返回 URL(/assets/logo-[hash].png)
- 小资源内联(base64,默认 4KB 阈值)
- 大资源独立文件 + 哈希
→ assets = 导入即处理,小内联大独立
产物的消费:
- dist/ 静态部署(Nginx/CDN)
- index.html 是入口(引用打包资源)
- 服务端渲染读 manifest(见下节)
- PWA/离线:清单中资源预缓存
→ 产物 = 一套可部署的「静态站点」
心智:产物三类:chunk(JS 打包文件:入口/动态/公共)、asset(CSS/图片/字体等资源)、entry(HTML 入口引用入口 chunk);chunk 用 import 表达依赖关系(动态 chunk 懒加载、公共 chunk 共享、modulepreload 预加载);命名 = 可读名 + 内容哈希(缓存友好);CSS 独立 asset 与 chunk 关联;静态资源小内联(<4KB)大独立哈希;产物是可直接部署的静态站点(dist 输出 + HTML 入口)。
7. manifest 与运行时注入
manifest:产物清单:
// vite.config.ts 开启
export default defineConfig({
build: { manifest: true }
})
// 生成 dist/.vite/manifest.json
{
"src/main.ts": {
"file": "assets/main-abc123.js",
"src": "src/main.ts",
"isEntry": true,
"imports": ["assets/vendor-xyz.js"]
}
}
manifest 的用途:
- 服务端渲染:SSR 框架读 manifest 找到产物文件
- 预加载:知道入口 chunk 的依赖(modulepreload)
- 校验:部署后确认资源存在
- 指纹:内容哈希用于缓存策略
→ manifest = 构建产物与「消费方」的接口
运行时注入(index.html):
<!-- Vite 构建时注入到 index.html -->
<script type="module" crossorigin
src="/assets/main-abc123.js"></script>
<link rel="stylesheet" href="/assets/index-def456.css">
<link rel="modulepreload" href="/assets/vendor-xyz.js">
<!-- crossorigin + modulepreload = 生产资源加载优化 -->
modulepreload 的优化:
- 入口 chunk 的依赖 chunk 预加载
- 提前发起请求(减少加载瀑布)
- 与动态 chunk 配合:预取懒加载 chunk(vite:preload)
- 浏览器缓存预加载资源(二次访问快)
→ preload = 提前拉取关键 chunk,减少等待
动态导入的预加载注入:
- 动态导入 chunk:vite:preload 自动注入预取
(静态已知的动态导入 → 注入 modulepreload)
- 运行时动态路径:无法预注入(glob 可部分)
- 平衡:预取太多 = 首屏请求爆炸
→ 预取 = 静态动态导入自动注入,控制数量
manifest 与 SSR 的配合:
// SSR 框架(如 vite-plugin-ssr)
const manifest = JSON.parse(fs.readFileSync(
'dist/.vite/manifest.json', 'utf-8'))
const entry = manifest['src/main.ts']
// 用 entry.file 作为 script src
→ SSR 用 manifest 把 HTML 与产物关联
manifest 的调试:
- 构建后检查 manifest.json 完整性(文件存在性)
- 对比 manifest 的 imports 与产物文件
- 部署后:资源 404 → 查 manifest 文件路径
- 多入口:manifest 覆盖所有入口
→ 调试 = manifest 与 dist 文件对比
心智:manifest = 构建产物清单(入口文件/依赖/哈希),build.manifest 开启,是产物与消费方(SSR/预加载/校验)的接口;index.html 运行时注入:crossorigin + modulepreload + CSS link;modulepreload 提前拉取关键 chunk 减少加载瀑布、动态导入由 vite:preload 自动预取(控制数量防请求爆炸);SSR 框架用 manifest 关联 HTML 与产物;调试 = manifest 与 dist 文件对比排查 404。
8. 浏览器目标与降级
build.target:目标浏览器:
// vite.config.ts
export default defineConfig({
build: {
target: 'es2020' // 默认 esnext(现代)
}
})
// 或 browserslist 字符串
target: ['es2020', 'safari14']
target 的作用:
- 决定产物「使用哪些 ES 特性」
- esnext:最新语法(现代浏览器支持)
- es2020:可选链、空值合并等
- 更低的 target:转换更多语法(体积大)
- 不匹配的语法会「转译/保留」
- 影响 chunk 大小与兼容性
→ target = 兼容性与体积的旋钮
target 与降级策略:
- 现代应用:esnext(体积最小,需现代浏览器)
- 企业/兼容:es2018/es2015(转译更多,体积大)
- 极端:es5(老浏览器,几乎全转)
- 分层:现代给现代、老浏览器给 legacy(见下)
→ 降级 = 按「目标受众」选 ES 水平
legacy 插件(旧浏览器降级):
- @vitejs/plugin-legacy:对老浏览器生成「旧版产物」
- 检测不支持现代语法 → 加载旧版
- 现代浏览器:现代产物(小快)
- 旧浏览器:legacy 产物(转译全、大)
- 原理:双产物 + 特性检测加载
→ legacy = 现代快 + 旧兼容的「双产物方案」
降级的影响评估:
- target 越低:转换越多、产物越大
- 转译:语法降级(async/await → generator)
- polyfill:新 API 需要 polyfill(core-js)
- 权衡:兼容受众 vs 体积性能
→ 评估 = 目标用户浏览器分布 + 体积预算
构建目标检查:
- 产物里搜索现代语法(?. ?? #field 等)
- 检查是否意外保留目标不支持的特性
- 用浏览器测试矩阵(chrome/safari/firefox)
- 生产监控:旧浏览器错误率
→ 检查 = 产物语法 + 浏览器实测 + 监控
心智:build.target 决定产物的 ES 特性水平(esnext 最小体积 / 更低转译更多体积),是兼容性与体积的旋钮;降级策略:现代应用 esnext、兼容需求转译更多;@vitejs/plugin-legacy 双产物方案(现代快 + 旧浏览器特性检测加载旧版);target 越低转换越多、需 polyfill 越大;评估 = 用户浏览器分布 + 体积预算;检查 = 产物语法搜索 + 浏览器测试矩阵 + 生产错误监控。
9. 构建诊断:debug 与日志
debug 构建:
# 打印构建调试信息
DEBUG=vite:build vite build
DEBUG=vite:resolve vite build
DEBUG=rollup vite build
# 组合:DEBUG=vite:*,rollup vite build
常用诊断信息:
- 解析:哪些导入被解析到哪(vite:resolve)
- 构建:构建阶段进度与时间(vite:build)
- Rollup:插件钩子执行细节(rollup:*)
- 警告:构建时的潜在问题(未压缩/重复)
→ debug = 定位「解析、阶段、钩子、警告」
构建错误的排查:
常见构建错误:
1. 解析失败:依赖缺失/exports 错误
→ 查 resolveId 日志 + package.json
2. 转换错误:语法/插件转换崩溃
→ 定位到出错模块(错误栈有文件)
3. 产物错误:chunk 异常/循环依赖
→ 查循环 import + manualChunks
4. 内存不足:大图/大文件
→ 减并行、提内存、拆构建
→ 错误 = 解析/转换/产物/资源 四类定位
循环依赖的检测:
- 循环 import:A → B → A
- 症状:产物中有未定义(TDZ)/执行顺序错
- 检测:Rollup 警告循环依赖(检查日志)
- 修复:重构(提取公共/延迟引用)
- 或:移动副作用初始化(避免顶层互依赖)
→ 循环 = 产物隐患,重构消除
构建耗时分析:
- 构建耗时:transform 是大头(见第 4 节)
- 工具:vite build 的 time 输出(阶段耗时)
- 插件耗时:各插件 transform 时间(日志)
- 优化:缓存、减少插件、esbuild 转译
→ 耗时 = 阶段/插件定位,针对性优化
产物验证:
- 构建后冒烟:打开 dist 首页(资源加载)
- 校验 manifest:文件存在 + imports 完整
- 检查 gzip 大小(传输体积预算)
- 多浏览器实测(兼容性)
→ 验证 = 构建成功 + 产物可跑 + 体积达标
心智:构建诊断 = DEBUG=vite:build|resolve|rollup 定位解析/阶段/钩子/警告;四类错误定位:解析失败(resolveId+exports)、转换崩溃(错误栈模块)、产物异常(循环依赖重构消除)、内存不足(减并行提内存);构建耗时大头是 transform(按阶段/插件定位优化);产物验证 = 冒烟 + manifest 校验 + gzip 体积 + 多浏览器实测。
10. 速查表与一句话记忆
全篇速查:
| 主题 | 结论 |
|---|---|
| 全链路 | 输入 → 建图 → 摇树分割 → 生成 |
| 两阶段 | build(建图)/ generate(出产) |
| 钩子 | resolveId/load/transform(build)+ renderChunk/generateBundle(output) |
| 解析加载 | resolve 定位、load 取内容、\0 虚拟模块 |
| transform | code in → code out,转换链顺序管道 |
| 打包 | 图 → 摇树 → chunk 分配 → 代码生成 |
| 产物 | chunk + asset + entry(HTML) |
| manifest | 产物清单,SSR/preload 接口 |
| target | ES 特性旋钮,legacy 双产物 |
| 诊断 | DEBUG + 错误四类 + 耗时分析 |
一句话记忆:Rollup 构建管线 = vite build 背后的打包内核:一次构建 = 输入入口 → resolveId(导入→id)→ load(id→源码)→ transform(转换+重扫依赖)→ 递归建全量模块图 → 摇树 → chunk 分配(入口 chunk + 动态独立 + 公共提取 + manualChunks)→ 代码生成 → 输出 + manifest;分 build(建图)与 generate(出产)两阶段,插件钩子也按阶段分——build 钩子 resolveId/load/transform/moduleParsed/buildEnd,output 钩子 renderChunk/generateBundle/writeBundle,约定不拦截返回 null、虚拟模块用 \0 前缀标记;transform 是转换链(多插件顺序管道 + 前输出后输入),也是构建耗时大头;产物三类:chunk(JS:入口/动态/公共,import 表达依赖)+ asset(CSS/图片,内容哈希 + 小内联)+ entry(HTML,crossorigin + modulepreload 注入);manifest(build.manifest)是产物清单,供 SSR/预加载/校验使用,vite:preload 自动预取动态 chunk;build.target 是 ES 特性旋钮(esnext 小体积/低 target 大体积),@vitejs/plugin-legacy 做现代+旧兼容双产物;诊断 = DEBUG=vite:build|resolve|rollup、四类错误定位(解析/转换/产物/内存)、循环依赖重构消除、构建耗时按 transform 定位;产物验证 = 冒烟 + manifest 校验 + gzip 预算 + 多浏览器实测。
延伸阅读
- /vite-build-optimization/ — 构建配置与产物优化
- /vite-import-graph-internals/ — 导入图与模块图
- /vite-tree-shaking-deep-dive/ — Tree-shaking 与产物瘦身
- /vite-plugin-development/ — 插件开发与钩子
- 前端工程专题 — 前端工程化与构建工具
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。