跳到主要内容

第 7 章 · 工程实践与选型

主线索:

按能力排,不按工具排。 跟着工具换代走,每次换工具都要重学配置;按能力排,只需学一遍。


7.1 顺序按能力排,不按工具排​

两种学习顺序​

路线顺序代价
按工具排(多数人的默认)Webpack → Vite → Rspack → ……每次换工具都要重学一遍配置,且旧知识迅速报废
按能力排建立图 → 会用默认值 → 会读产物 → 会算账 → 会迁移学一遍,跨工具通用

为什么"按工具排"是陷阱:因为工具名会变,机制不会变。

依赖图、Loader/Plugin 分工、Tree Shaking 两阶段、代码分割三策略 —— 这些在 Rspack、Rolldown、Turbopack 里全都还在,只是换了执行者(§4.5 的结论)。

你花时间背的 optimization.splitChunks 字段默认值会过期;你理解的"为什么需要抽公共包"不会。


7.2 五个台阶与判定标准​

台阶表​

台阶目标学什么判据常见跑偏
1 建立图通用心智模型依赖图 / Loader·Plugin / Tree Shaking / 代码分割说清「源码 → 依赖图 → chunk → 产物」背配置项
2 会用默认值跑通真实项目vite.config.ts 最小配置、.env + import.meta.env、build.sourcemap、server.proxy每条配置项能说出「为什么存在」一上手就装一堆插件
3 会读产物 ⭐从使用者变成工程化读 stats / 产物分析、chunk 划分、hash 稳定性改一行代码能预测哪些产物 hash 会变;错了能解释为什么只看体积,不看来源
4 会算账结论站得住六步测量法、三层缓存、指标拆分听到「变快了」能拆出绑定的指标与前提拿厂商数字当结论
5 会迁移选型对存量下判断插件清单核对、产物回归对比、回退路径设计给得出「能不能迁、要多久、翻车了怎么办」先比技术优劣,不问痛点

第 3 级为什么是分水岭​

前几级的判据是「我说得清」;第 3 级的判据是「我预测得准」。

级别判据的性质
1、2 级陈述性 —— 能复述、能解释
3 级预测性 —— 能对未发生的事给出可验证的判断
4、5 级预测性 + 决策性

"改一行代码能预测哪些 hash 会变"这个判据的威力:它一次性检验了「依赖图 + chunk 来源 + hash 计算范围 + 缓存三件套」四块知识。你预测错了,就一定有一块没真正理解。

不读产物,所有优化都是猜的。

依赖关系​

  • 1 → 2 → 3 必须串行(后面的判据依赖前面的能力)。
  • 4、5 可以在真实项目需求出现时再补。
  • 不要为了"学完"而学。

7.3 每阶段的动手项目​

配项目的两条标准​

① 项目要小到一次做完 ② 必须能自证过关(有判据,不靠感觉)

"能自证"是更重要的那条 —— 没有判据的项目,做完也不知道自己会不会。

五个项目​

台阶动手项目规模过关判据
1手写迷你打包器≤100 行跑通「两个文件互相 import」;能解释共享模块为何只处理一次
2零插件 Vite 项目1 小时每行配置能说出「为什么存在」;删掉配置项目仍能跑
3hash 预测游戏(三页面项目)半天改之前先写预测,连续 5 次正确
4构建基准测量(六步法)半天结论每句都带前提
5迁移评估一页纸半天结论含「翻车了怎么办」

每条的细节​

台阶 1 · 手写迷你打包器

  • 目的不是做一个能用的打包器,是验证依赖图不是背的。
  • 最小实现:读入口 → 找 import → 递归解析 → 拼成一个函数表 → 输出一个文件。
  • 判据里的"共享模块为何只处理一次",直指 §2.1 的"图 ≠ 树" —— 你的迷你实现必须自己处理这个去重,才能真正体会。

台阶 2 · 零插件 Vite 项目

  • 反直觉动作:故意删掉所有配置,看还能不能跑。
  • 你会发现大部分配置本来就不需要 —— 这正是 §3.3「Vite 默认值覆盖了语言层」的体感验证。

台阶 3 · hash 预测游戏(本书最推荐的一项)

  • 准备一个三页面的小项目。
  • 分别做四种改动,每次改前先写下预测(哪些产物的文件名会变),改完对照:
改动类型预测时应考虑
改业务代码该模块所在 chunk 变化
改依赖版本相关 vendor chunk 变化
改 CSS样式产物 + 引用它的 chunk
只改注释应该什么都不变吗? —— 如果 hash 变了,说明缓存三件套没配齐
  • 加分项:装 rollup-plugin-visualizer,看 vendor chunk 里的模块排行 —— 这一步顺手把"会读产物"练了。

台阶 4 · 构建基准测量

  • 按 §5.6 的六步法跑一遍,产出一段带前提的结论文字。
  • 判据不是"数据好看",是"每句都带前提"。

台阶 5 · 迁移评估一页纸

  • 五段结构:
段落内容
① 依赖清单现有的 loader / plugin 清单
② 硬阻塞有没有用到不支持的能力
③ 产物回归方案怎么证明迁移后产物没变坏
④ 回退路径翻车了怎么退回去
⑤ 结论能不能迁、要多久
  • 判据是第 ④ 段 —— 没有回退路径的是假报告。

一个自检问题​

五个项目里,哪个最容易被自己"糊过去"(明面做了、实际没拿到能力)?

答案是台阶 2。因为它最容易"跑起来就算完成" —— 建个项目、npm run dev 能开,就以为过关了。但判据是"每行配置能说出为什么存在"+"删掉配置还能跑" —— 加这两个判据,就是堵住糊弄的办法。

通用规则:先写判据,再动手。


7.4 文档与源码的阅读切入点​

文档:用来查证,不是用来读完​

两种失败的读法:

  • 从第一页读到最后一页(读不完,且大部分用不上)
  • 只搜 API(知道字段,不知道为什么)

三种正确读法:

读法用法
① 查 API只搜,不读目录
② 读机制页只有少数几页值得逐字读:Webpack Concepts、Vite Why Vite 与 Dependency Pre-Bundling、Rollup Plugin Development
③ 读迁移指南 ⭐性价比最高 —— 写的是官方承认的差异,等于一份免费的对照表

为什么迁移指南性价比最高:因为它是官方替你总结好的差异清单。你不用自己发现"Rspack 和 Webpack 在哪些地方不一样" —— 官方文档里已经列出来了。Rspack 的 migration 页 + rspack-compat 仓库就是典型。

读文档的判据:

读到「已知限制 / deprecated」段落时,停下来精读。

那是坑的官方来源 —— 官方主动告诉你"这里有问题""这个要废弃了",比任何第三方文章都权威。

源码:别从 index.js 读起​

三个切入点:

切入点门槛能验证什么
① 产物反推低读 dist/ 的 runtime 胶水代码 → 直接看到动态 import 与 HMR 的实现证据
② 插件钩子中挑最熟的官方插件,只读钩子函数体,看它接收什么、返回什么
③ 具体 bug高先搜报错字符串 → 顺调用栈往回读那一条路径

为什么推荐从产物反推开始:产物是你自己的代码生成的,你熟悉它应该长什么样。看到 runtime 里那段 __webpack_require__ 的胶水代码,你能直接对应上 §2.2 讲的"运行时胶水污染 chunkhash"。

铁律:

只读你有一个具体问题的那条路径。 漫读源码是最低效的学法。

两条纪律:

  1. 先读类型定义 / 接口描述(地图),再读实现(地形)。
  2. 读完必须留下一个可验证的结论。 写不下「我验证了 X 是 Y」= 没读。

7.5 选型决策矩阵​

本节是全书选型内容的唯一决策出口。§3.6 负责说明"为什么",本节负责"怎么办"。

第一步:顺序比结论重要​

决策顺序:先看有无迁移成本 → 再看有无硬阻塞 → 最后才比成本收益。 不是先比技术优劣。

为什么第一步是"有没有痛点":

没有痛点,ROI 的收益侧是空的 —— 你没法计算"获得了多少",因为你没有要解决的问题。而迁移成本是实打实的(功能冻结、回归风险、CI 改造、下游产物影响)。

关于 ROI 的实践警告:ROI 标准在实践中很难用准 —— 收益是感受性的、隐性成本容易被低估(功能冻结期、回归风险、CI 改造、下游依赖产物结构的人)。所以把它当思考框架用,别当计算公式用。

场景对照表​

情境建议理由
全新项目Vite约定完备、冷启动快、生态成熟
中小存量项目、无硬阻塞保持 Webpack迁移成本 > 收益
存量项目、build 成瓶颈评估 Rspack配置接近 Webpack,可渐进迁移
必须深度定制产物结构Rspack / Webpack产物编排与干预点更强
多页 / 微前端看生态,常落 Webpack / Rspack对插件生态与产物控制要求高
需兼容旧浏览器Webpack,或 Vite + @vitejs/plugin-legacyVite dev 依赖原生 ESM
库 / 组件包Vite library mode,或 tsup / unbuild需求单一,够用

三条原则​

  1. 新项目默认 Vite
  2. 存量项目不轻易迁移
  3. 只在有明确证据「Vite 不够用」时才换

组件库场景的硬坑​

这一节值得单独记,因为它的需求与"应用"完全不同。

坑正确做法
Vite 默认不生成 .d.ts用 vite-plugin-dts,或单独 tsc --emitDeclarationOnly
build.lib.formats 需显式声明写 ['es', 'cjs'](Vite 5 后默认只出 ESM)
peer 依赖被打进产物必须 rollupOptions.external 掉(否则产物里带一份 React)
入口字段没对齐package.json 的 main / module / types / exports 要手动对齐产物

为什么"组件库选 Vite"的理由不能照抄"应用选 Vite"的理由:

应用关注组件库关注
冷启动快不快 ❌ 不相关产物格式(ESM + CJS)
生态成不成熟 ⚠️ 弱相关类型声明(.d.ts)
—外部依赖不打包(external peer)
—入口字段对齐(main/module/types/exports)

组件库在 CI 里跑 build,没有 dev server —— 所以"冷启动快"对它几乎没有意义。这是"复用上一个场景的答案"最典型的翻车现场。

通用动作:回答前先问 —— 这个场景和上一个场景,哪里不一样?


7.6 迁移评估:一页纸模板​

判断迁移该不该做,写一页纸就够了。 写不满一页说明想得不清楚;超过一页说明在凑字数。

# <项目名> 构建工具迁移评估

## 1. 现状与痛点(必须有具体数字)
- 当前工具与版本:
- 生产构建耗时:___ 秒(N 次中位数,机器规格:___,代码规模:___ 行 / ___ 依赖)
- 痛点场景:CI 等待 / 本地反馈慢 / 其他

## 2. 依赖清单核对
| 现有 loader / plugin | 目标工具是否支持 | 替代方案 |
|---|---|---|
| | | |

## 3. 硬阻塞
- 浏览器兼容要求:
- 是否使用目标工具不支持的能力:
- 若有阻塞 → 结论:不迁移 / 换方案

## 4. 产物回归方案(怎么证明没变坏)
- 产物结构与体积对比方式:
- 验收判据:
- 灰度 / 双跑方式:

## 5. 回退路径(翻车了怎么办)
- 回退触发条件:
- 回退动作:
- 回退耗时:

## 6. 结论
- 能不能迁:
- 预计耗时:
- 前置条件:

第 5 段是关键。 没有回退路径的评估报告是假报告 —— 它假设了迁移一定成功,而这在工程上不成立。

第 1 段也必须带数字。 "构建很慢"不是痛点,"生产构建 4 分 20 秒、每次 CI 阻塞发布"才是。


7.7 本章常见误区(18 条)​

#误区正确认知
1按工具换代排学习顺序按能力排,配置只学一遍
2把「能跑起来」当学会了判据是「你能预测什么」
3跳过第 3 级(读产物)不读产物,所有优化都是猜的
4第 2 级就开始装插件默认值没吃透 = 不知道基线
5拿厂商基准数字当自己的结论厂商数字是起点,不是判据
6以为五台阶必须全部走完1→2→3 串行;4、5 按需补
7项目做得太大目的是验证概念,不是做产品
8没有判据就动手先写判据,再动手
9台阶 1 跳过不写它的价值是验证依赖图不是背的
10台阶 3 只看体积数字hash 变化才是因果
11台阶 4 只跑一次单次测的是系统负载,不是构建性能
12台阶 5 写了「能迁」就收工没有回退路径的是假报告
13想「把文档读完」文档是字典,不是教材
14只读 API 不读机制页知道字段,不知道为什么
15忽略迁移指南官方承认的差异清单,白送的对照表
16从 index.js 漫读源码先有问题,再读源码
17读源码不读类型定义地图先于地形
18读完不留可验证结论写不下「我验证了 X 是 Y」= 没读

本书的三条带走结论​

如果只记三句话,记这三句:

1. 默认即权力 —— 强制 = 默认 + 高摩擦回退。

2. 兼容还是另起,取决于用户有没有选择权。

3. 知识折旧率按层排序 —— 原理层最慢、打包层最快。 因此第 2 章折旧最慢、回报最高。


回到开头:README · 目录