Rollup 构建管线深入:插件钩子、产物结构与 manifest

深入 Vite 生产构建背后的 Rollup 管线:从入口到产物的完整链路、Rollup 插件钩子体系(resolveId/load/transform/模块图钩子/输出钩子)、解析与加载阶段、转换阶段、打包与代码生成阶段、产物结构(chunk/asset/entry 的关系)、manifest 与运行时注入、浏览器目标与降级策略、以及构建诊断(debug 与日志)的实践,帮助你理解构建产物如何产生并排查构建问题。

引言

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. 从入口到产物:构建全链路

一次 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 虚拟模块
transformcode in → code out,转换链顺序管道
打包图 → 摇树 → chunk 分配 → 代码生成
产物chunk + asset + entry(HTML)
manifest产物清单,SSR/preload 接口
targetES 特性旋钮,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/ — 插件开发与钩子
  • 前端工程专题 — 前端工程化与构建工具

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「vite」更多文章

  1. 包体分析与性能监控:Bundle Analyzer、性能预算与门禁
  2. 组件库开发指南:Vite 库模式、发布 npm 与按需加载
  3. React 应用架构模式:目录结构、状态管理与性能优化