Yarn 和 npm 在依赖提升策略上有什么不同?
1. npm 的扁平化提升
- npm 采用扁平化(flat)的 node_modules 结构,尽可能多地把包提升到顶层目录以减少重复
- 这种做法简单且兼容几乎所有工具,但会带来幽灵依赖(phantom dependency)问题:代码可以 require 到从未在 package.json 中声明、却恰好被提升到顶层的包
- 一旦传递依赖调整了自己的依赖,这类未声明的引用就可能突然失效
2. Yarn 的提升与隔离策略
- Yarn Classic 同样使用扁平化的 node_modules,提升逻辑与 npm 相近,也存在类似的幽灵依赖风险
- Yarn Modern 的 Plug'n'Play 模式则彻底摒弃了 node_modules,改用 .pnp.cjs 映射文件直接回答"某个包在哪里"的问题,从机制上消除了幽灵依赖
- 在 node-modules 链接器模式下,Yarn 也提供了 nmHoistingLimits 配置来限制提升范围,收紧依赖可见性
3. 确定性保障的差异
- Yarn 很早就强制严格的确定性,Modern 的实现更严格——默认情况下若 lockfile 会被修改就直接拒绝安装,把意外变更拦截在生产之前
- npm 5+ 也采用了 lockfile 机制,但其确定性更多依赖 package-lock.json 对目录形状的完整描述
- 在实际工程中,Yarn 的提升算法会综合考虑 optionalDependencies、devDependencies 等因素,即使它们未被安装也会影响普通依赖的提升位置,从而减少开发与生产环境的布局差异