章节摘要:这一章把包管理器的两台核心引擎——依赖解析器与目录布局器——拆开勘验。解析器负责把清单里的版本范围翻译成精确版本,布局器负责决定每个包在磁盘上的安放位置。前者是算法题(怎么在约束里求解),后者是工程题(怎么摆放才兼顾复用与隔离),而幽灵依赖、路径过长、版本冲突排查这些高频事故,根子都在这两台引擎的配合缝隙里。读完本章,你对 node_modules 的任何一个"奇怪现象"都应能说出因果。
一句总纲:解析决定"装哪个版本",布局决定"装在哪个位置"——两者独立决策,但事故往往出在决策的交互处。解析器按约束求解出一份版本方案,布局器再按提升规则把方案摊到磁盘;任何一环的"合理默认",在另一环眼里都可能变成意外。
注意图中唯一的分叉:约束冲突时的回溯重试。多数人以为解析是"查表",实际上它是一次带回溯的搜索——这也解释了为什么复杂依赖图的解析有时明显变慢,以及为什么同样的清单在不同时间会解出不同方案(候选版本集合变了)。
2.1 依赖树的构建算法:从一份小清单出发,手工走完"收集约束、查询候选、求解版本"的全程,看清解析器做决策的每一步。附带讲解去重与树剪枝:解析不是只求有解,还要让落盘份数尽量少。
2.2 语义化版本约束深度解析:把 ^、~、区间、通配符的语义一次讲透,配一份数轴图。版本声明是解析器的输入语言,读不懂区间语义,后面所有"为什么装了这个版本"的问题都无从谈起。
2.3 node_modules 物理布局与提升算法:勘验嵌套与扁平两代布局,逐条拆解提升规则与 deduped 的判定条件。本节是理解"目录为什么长这样"的总答案。
2.4 幽灵依赖现场还原与根治:把前两节的机制拼成一次完整事故——幽灵依赖怎么从提升规则里长出来、怎么在 CI 上爆雷、又该怎么根治。本章机制落地的收口之作。
[2.1 解析算法] 输出:精确版本方案 | v [2.2 版本语义] 是 2.1 求解的输入语言,先修更顺 | v [2.3 布局规则] 消费 2.1 的方案,产出磁盘结构 | v [2.4 幽灵依赖] 2.2 的宽松语义 与 2.3 的提升规则 相乘的产物
四节的依赖关系建议照此顺序学:2.2 讲的区间语义是 2.1 求解的输入,2.1 的方案是 2.3 摆放的原材料,2.4 则是前面所有"合理默认"叠加出的意外。单看 2.4 会觉得幽灵依赖是玄学,学完前三节再看它,只是两台引擎一次普通的配合失误。
前置:第一章的历史脉络(尤其三代扁平化的动机)、基础命令行操作。若你已熟悉 SemVer 语义,2.2 可快速过,但建议至少读完"区间在解析器眼里的形态"一段——那段决定了你对"为什么半年后版本变了"的直觉。
后续延伸:本章产出的"版本方案"正是第三章锁文件要冻结的对象——锁文件里每条记录都对应本章解析结果的一行;而布局规则的另一个变体(严格隔离、无 node_modules 路线)在第五章 5.2 展开。学完本章再看锁文件,会有"原材料终于进厂"的连贯感。
本章金句:目录里的每一层嵌套、每一处 deduped、每一个"没声明却能引入"的包,都是解析与布局两台引擎握手时留下的手印。