目录一、npm、npx 和 pnpm 的关系1、三者之间的关系2、三者的历史脉络二、npm 和 pnpm 的对比1、npm 和 pnpm 的语法“大差不差”2、npm 和 pnpm 的核心差别来自于“底层实现”1、底层项目结构不同2、“依赖严格性” 不同3、npm 和 pnpm 的 lockfile 也是不同的三、如何选择究竟该用 npm 还是 pnpm 呢一、npm、npx 和 pnpm 的关系1、三者之间的关系Node.js │ ├── npm ─────────────── 包管理器 npm Registry 生态 │ │ │ └── npx ───────── “执行 npm 包里的 CLI”工具 │ └── pnpm ────────────── npm 的竞争型包管理器 仍然大量使用 npm Registry也就是说npm 和 pnpm 是同一层级的包管理器而 npx 是执行器。2、三者的历史脉络把十几年压缩成一张图就是2009 │ │ Node.js 生态出现 ↓ npm │ │ “怎么安装、发布、管理 JS package” │ ├───────────────────────────────┐ │ │ ↓ ↓ 2015/2016 2017 pnpm npx │ │ │ “node_modules “CLI 为什么 │ 能不能更高效、严格” 非得全局安装” │ │ ↓ ↓ content-addressable store 临时执行 CLI strict node_modules 执行本地 CLI workspace │ │ ↓ ↓ 现代 pnpm npm 7 │ ↓ npm exec ↑ npx因此从“思想演变”看其实非常清楚npm 解决 “包怎么管理” npx 解决 “包里的程序怎么方便执行” pnpm 重新追问 “包管理这件事本身能不能做得更高效、更严格、更适合大型工程”最合理的理解不是“pnpm 把 npm 和 npx 淘汰了”而是npm Node.js / JavaScript 包生态的重要基础 npx npm 体系里的 package runner pnpm 在 npm package 生态之上 提供另一套更现代的依赖管理方案你自己的项目采用 pnpm 后日常操作就可以进一步收敛成npm install → pnpm install npm install xxx → pnpm add xxx npm exec xxx → pnpm exec xxx npx xxx → pnpm dlx xxx生态仍然是 npm 生态日常工具换成 pnpm。这句话基本就把三者十几年的关系讲透了。至此我们接下来只研究npm和pnpm。二、npm 和 pnpm 的对比npm 和 pnpm 的最重要的区别npm 更通用、更默认pnpm 更省空间、更严格、Monorepo 体验更强。对比项npmpnpm定位Node.js 默认包管理器第三方高性能包管理器上手难度最低很低安装速度快通常更快磁盘占用相对更大更省依赖存储每个项目各自安装为主全局内容寻址 Store 复用node_modules更扁平更严格、基于链接组织幽灵依赖更容易出现更容易避免lockfilepackage-lock.jsonpnpm-lock.yamlWorkspace支持支持而且体验很好Monorepo能用很强生态兼容最高很高默认可用性安装 Node 后通常就有通常额外启用/安装推荐场景普通项目、教程、通用环境中大型前端项目、Monorepo1、npm 和 pnpm 的语法“大差不差”最直观的区别先看命令。npm 对应npm install npm install axios npm install -D typescript npm uninstall axios npm run devpnpm 对应pnpm install pnpm add axios pnpm add -D typescript pnpm remove axios pnpm dev日常使用上其实差别并不大。真正的差别在底层。2、npm 和 pnpm 的核心差别来自于“底层实现”1、底层项目结构不同npm 的思路可以粗略理解成项目 A └── node_modules └── react 项目 B └── node_modules └── react同一个依赖不同项目里可能各有一份。pnpm 则更像pnpm store │ react ↙ ↘ 项目 A 项目 B相同内容可以被多个项目复用所以如果你电脑上有很多前端项目pnpm 的磁盘优势会比较明显。2、“依赖严格性” 不同另一个关键区别是依赖严格性。假设你的依赖关系是你的项目 ↓ package-a ↓ lodash但你的package.json没有声明 lodash。理论上你自己的代码不应该直接import lodash from lodashnpm 在某些依赖提升布局下这种代码有可能“碰巧能用”。这就属于phantom dependency 幽灵依赖pnpm 默认更严格通常会更早暴露这种问题。所以 pnpm 的理念更接近你声明了什么依赖 就使用什么依赖这对大型项目反而是好事。在 Monorepo 场景里pnpm 的优势更明显。比如project/ ├── apps/ │ ├── web/ │ └── admin/ ├── packages/ │ ├── ui/ │ ├── utils/ │ └── types/ └── pnpm-workspace.yaml你可以pnpm --filter web dev只启动 web。或者pnpm --filter web add axios只给 web 安装 axios。也可以pnpm -r build整个 workspace 批量构建。这类体验通常是 pnpm 非常有竞争力的地方。不过 npm 也有非常明确的优势。最大的优势就是默认 通用 兼容性最高 文档最多你去任何 Node.js 项目、服务器、CI 环境最容易直接遇到的是npm而不是 pnpm。很多第三方文档第一版命令也通常写npm install npm run dev因此 npm 的价值不在于“比 pnpm 更先进”而在于npm 是整个Node.js / JavaScript 包生态的“基础通用语言”。3、npm 和 pnpm 的 lockfile 也是不同的lockfile 也要区分。npmpackage-lock.jsonpnpmpnpm-lock.yaml项目如果确定使用 pnpm最好就统一package.json pnpm-lock.yaml不要团队里有人npm install另一些人pnpm install然后仓库里同时出现package-lock.json pnpm-lock.yaml这是比较典型的工程坏味道。三、如何选择究竟该用 npm 还是 pnpm 呢如果从选型角度看可以很简单教程 / 小 Demo / 临时项目 → npm 足够 普通 React / Vue 项目 → npm、pnpm 都行 长期维护的现代前端项目 → 更推荐 pnpm Monorepo → 优先考虑 pnpm 陌生老项目 → 看项目现有 lockfile不要擅自换对你这种前端开发场景我会建议一个原则知识体系以 npm 为基础实际项目以 pnpm 为主。也就是npm必须懂 pnpm主力用因为以后你看文档、面试、理解 Node 生态npm 绕不过去但真正自己搭项目尤其后续进入 Workspace、Monorepo、组件包拆分时pnpm 通常更舒服。