现代 JavaScript 项目几乎离不开包管理器。npm 作为默认之选已经存在了十多年,而 yarn 和 pnpm 则在不同的时间点针对 npm 的痛点提出了各自的解决方案。三者都能安装依赖、管理版本、运行脚本,但底层机制上的差异会直接影响项目的磁盘占用、安装速度、依赖安全性和开发体验。理解这些差异,才能根据团队现状和项目需求做出明智的选型。
20.1.1 npm:生态的根基,不断进化的默认选项
npm(Node Package Manager)是 Node.js 自带的包管理工具,也是全球最大的开源软件注册中心。从 npm v3 开始,依赖被拍平(flatten)到 node_modules 顶层,尽量避免嵌套太深;v5 引入了 package-lock.json 锁定依赖树;v7 开始正式支持 workspaces,具备了基础的 monorepo 能力。
优点:
- 与 Node.js 深度绑定,无需额外安装。
- 生态最完整,任何 Node.js 环境都能直接运行 npm 命令。
- 近年更新频繁,很多早期被人诟病的问题(如安装慢、不确定性)已大幅改善。
缺点:
- 即使已经拍扁,安装速度仍可能比 yarn/pnpm 慢,尤其冷安装(无缓存)时。
- 早期版本(v3 之前)的嵌套结构会造成 Windows 路径过长等问题,但现代版本已基本解决。
- 依赖扁平化虽然减少了嵌套,却带来了“幽灵依赖”(phantom dependencies),即项目可以访问未显式声明的依赖,增加了脆弱性。
20.1.2 Yarn:稳定与确定性优先
Yarn 由 Facebook 于 2016 年推出,直接回应了当时 npm 在速度、安全、一致性方面的不足。它的并行下载和离线缓存让安装速度大幅提升,同时推出的 yarn.lock 文件确保了不同环境下依赖树完全一致。Yarn 后续发展出经典版(v1)和重写内核的 Berry(v2+),后者引入了即插即用(PnP)模式,彻底摆脱 node_modules。
优点:
- 并行下载、离线缓存,安装速度通常优于经典 npm。
- 严格的锁文件和确定性的依赖解析,适合 CI/CD 环境。
- Workspaces 原生支持,对 monorepo 友好。
- 输出清晰简洁,命令相较于 npm 更直观(但近些年 npm 已逐渐对齐)。
缺点:
- 需要额外安装,增加了团队环境搭建的一步。
- Yarn Berry 的 PnP 模式虽然先进,但兼容性至今仍有问题,很多传统工具和库并未针对 PnP 进行适配。
- 分裂的历史(v1 与 v2+)让社区文档存在断层,团队选型时需要明确版本。
20.1.3 pnpm:节省磁盘空间、消灭幽灵依赖
pnpm(performant npm)是目前增长最快、在大型项目中被越来越多采纳的工具。它的核心创新在于基于内容寻址的存储 + 硬链接机制:所有包的版本文件全局缓存一份,项目的 node_modules 中仅为实际用到的版本创建硬链接(或软链接)。这带来了两个质的改变:
- 磁盘空间极省:同一台机器上多个项目依赖相同版本的包时,磁盘上只保存一份实际文件。
- 严格的依赖隔离:pnpm 创建的
node_modules并非扁平结构,而是通过软链接模拟树形结构,项目无法访问未声明的依赖,完全消灭幽灵依赖问题。
优点:
- 安装速度极快,尤其冷安装时直接从全局存储硬链接,避免重复下载。
- 磁盘占用极小,适合 monorepo 或多项目并存的环境。
- 严格的依赖结构逼迫开发者显式声明所有实际依赖,提高代码健壮性。
- Workspaces 原生支持,并且 monorepo 环境下共享依赖时优势更加突出。
缺点:
- 因为依赖隔离更严格,有时用到了某些包隐式引用的子依赖会直接报错,需要开发者显式安装,这可能给从 npm/yarn 迁移过来的项目带来短期的调整成本。
- 某些极老的 npm 包可能会依赖扁平结构才能正常工作(极少见,但存在)。
20.1.4 选型建议
没有放之四海皆准的最佳包管理器,只有最适合当前团队和项目的选择。下表中的对比可以帮助决策:
| 特性 | npm | Yarn Classic (v1) | Yarn Berry (v2+) | pnpm |
|------|-----|-------------------|------------------|------|
| 安装速度 | 中(近年提升明显) | 较快 | 快(PnP 下极快) | 快(硬链接极速) |
| 磁盘效率 | 一般(拍平,重复安装) | 一般 | 较好 | 极优(全局存储复用) |
| 幽灵依赖 | 存在 | 存在(经典模式) | PnP 模式无 | 无 |
| 生态兼容性 | 最佳 | 好 | PnP 模式有兼容问题 | 较好(严格模式可能调整) |
| 内置 Node.js | 是 | 否 | 否 | 否 |
| Monorepo 支持 | 基础支持(v7+) | 好(workspaces) | 好(workspaces) | 好(原生 + 扩展) |
选型思路:
- 如果是个人小项目或完全使用默认习惯的团队,npm 已经足够好,并且省去安装额外工具的步骤。
- 如果项目对安装稳定性和确定性要求极高(如大型 CI/CD 流程),或者已经在用 yarn v1 且体验稳定,继续用 yarn 经典版不失为稳妥选择。
- 如果希望尝试更前沿的模式且愿意应对潜在兼容挑战,yarn Berry 的 PnP + 零安装方案值得探索。
- 如果是 monorepo、同一个机器上管理大量项目,或者团队对依赖安全和磁盘空间敏感,pnpm 是现代最佳选择,目前也被越来越多的开源项目(如 Vue、Vite)采用作为默认包管理器。
从 npm 到 pnpm 的迁移通常只需运行 pnpm import 根据现有锁文件生成 pnpm-lock.yaml,成本较低。无论选哪一方,都建议锁定工具版本并统一团队规范,避免混用带来的锁文件冲突。