npm 从 2009 年起就是 Node.js 的默认包管理器。但项目规模一膨胀, 它的固有问题就藏不住了:磁盘空间浪费、安装缓慢、幽灵依赖。 2017 年出现的 pnpm 用一套全新的依赖管理机制,对这些痛点发起了一次「降维打击」。

原文发布在掘金 · 四维度对比 + 选型建议

下面是结论与关键数据的整理,完整的对比表、实测来源与选择建议请到掘金阅读。

在掘金阅读全文

一句话要点

  • npm 的扁平化依赖树带来幽灵依赖:代码能访问没在 package.json 里声明的包,据统计约 30% 的 npm 漏洞源于此。
  • pnpm 用内容寻址存储 + 硬链接 + 符号链接,同一个包在任意项目里只存一份。
  • pnpm 的项目只能访问自己声明过的依赖,从架构层面消灭幽灵依赖。

一、依赖管理:扁平化 vs 虚拟存储

npm v3 之后把所有依赖提升到 node_modules 根目录,本意是解决早期嵌套过深导致的 「路径地狱」,但带来两个新麻烦:

  • 重复安装:10 个项目共用同一个 lodash,就在磁盘上存 10 份;同名不同小版本还会同时存在。
  • 幽灵依赖:项目代码能访问到那些并未声明的依赖包,导致构建行为不可预测。

pnpm 采用内容可寻址存储:所有包统一放在全局 .pnpm-store, 安装时用硬链接链到项目的 node_modules; 再用符号链接构建出精确的依赖关系图。 最终 node_modules 根目录下只有项目直接声明的依赖,其余都待在 .pnpm 的虚拟位置。

还有一个更精妙的细节:同一个包的不同版本之间,只有差异文件才会被新增进存储。 一个 100 文件的包,新版本只改了 1 个文件,那 pnpm 就只多存这 1 个文件。

二、磁盘占用与安装速度

  • 实测 create-react-app 初始化项目:npm 占 240MB,pnpm 只需 85MB。
  • 更极端的场景:npm v8 占 320MB 的项目,pnpm v7 只需 28MB。
  • 大型 Monorepo 中,pnpm 的磁盘占用可减少 60% 以上。
  • 包含 1200 个依赖的 Monorepo,pnpm 冷安装速度约为 npm 的 2.3 倍。
  • 「有缓存 + 有 lockfile + 有 node_modules」的最优场景下:pnpm 493ms vs npm 1.2s。

原因不复杂:pnpm 的核心操作是创建硬链接与符号链接,几乎不涉及文件物理复制,I/O 等待极少。

三、安全性:隔离带来天然优势

npm 虽然提供 audit 检测已知漏洞,但扁平化结构本身扩大了攻击面。 pnpm 的隔离式存储让每个包只能访问自己声明的依赖——即使某个依赖被恶意篡改, 攻击也无法扩散到未声明它的包。此外还内置了哈希校验、签名验证与锁文件完整性三重防护。

四、功能对比

功能                 pnpm              npm
Workspace(Monorepo) 支持              支持
隔离式 node_modules   默认              不支持
内容可寻址存储        支持              不支持
Plug'n'Play           支持              不支持
依赖补丁(Patching)  支持              不支持
Node.js 版本管理      支持              不支持
依赖清单(Catalogs)  支持              不支持
副作用缓存            支持              不支持

尤其对 Monorepo 的支持,pnpm 的 workspace: 协议能实现精准的依赖控制。 相比 lerna + yarn 的组合,CI 构建时间可缩短 45%,本地开发启动速度提升约 3 倍。

五、什么时候还用 npm

  • 小型项目或原型验证:依赖少的时候 pnpm 优势不明显,npm 的简单直接更合适。
  • 深度依赖 npm 生态插件的项目:部分工具链对符号链接结构存在兼容性问题。
  • 团队还没准备好迁移:迁移有时间成本,要先算清楚投入产出比。

小结

从「扁平化依赖树」到「虚拟存储 + 硬链接」,这不只是工具的迭代, 而是依赖管理范式的升级。 正如 pnpm 的全称——Performant npm,它正在重新定义「高性能包管理」的标准。

© 2026 HOLZWEGE 返回随笔列表