pnpm install 和 npm install 的区别
pnpm install 和 npm install 的区别
pnpm install
和 npm install
都是用于安装项目依赖的命令,但它们在依赖管理机制、磁盘空间占用、安装速度和依赖安全性上有显著区别:
核心区别总结
特性 | npm | pnpm |
---|---|---|
依赖存储 | 平铺结构(嵌套改进版) | 硬链接 + 符号链接(内容寻址存储) |
磁盘空间 | 依赖可能重复占用空间 | 全局共享依赖,节省 50%+ 空间 |
安装速度 | 较慢(需复制文件) | 更快(硬链接代替复制) |
依赖隔离 | 依赖可访问未声明的包(提升问题) | 严格隔离(符号链接隔离依赖) |
Monorepo 支持 | 一般(需工具辅助) | 原生优化(高效共享依赖) |
详细解析
1. 依赖存储方式
-
npm(v3+):
使用 平铺结构(flat node_modules)。
所有依赖(包括子依赖)被提升到node_modules
根目录,但某些依赖仍会嵌套安装。
问题:依赖可能非法访问未声明的包(依赖提升导致),且不同项目的依赖无法共享。 -
pnpm:
使用 硬链接(hard links) + 符号链接(symlinks):- 所有依赖包存储在全局仓库(类似缓存,默认
~/.pnpm-store
)。 - 项目
node_modules
中只包含符号链接,指向全局仓库的文件(硬链接)。 - 每个依赖都严格隔离,只能访问其声明的子依赖(通过嵌套的
.pnpm
目录实现)。
优势:避免依赖重复,严格隔离依赖环境。
- 所有依赖包存储在全局仓库(类似缓存,默认
2. 磁盘空间占用
- npm:
每个项目独立安装依赖,即使多个项目使用相同依赖,也会重复占用磁盘空间。 - pnpm:
全局仓库共享依赖,相同依赖只存储一份(硬链接本质是同一文件的多个引用)。
效果:可节省 50%+ 的磁盘空间,尤其适合 Monorepo 或多项目环境。
3. 安装速度
- npm:
需要下载并复制所有依赖文件到node_modules
(网络和磁盘 I/O 是瓶颈)。 - pnpm:
- 如果全局仓库已有依赖,直接创建硬链接(几乎瞬间完成)。
- 仅需下载缺失的依赖。
效果:安装速度通常快于 npm(尤其在依赖已存在时)。
4. 依赖安全性
- npm:
依赖提升可能导致 幽灵依赖(Phantom Dependencies):
未在package.json
声明的包(被提升到根目录)可能被代码非法访问。 - pnpm:
通过符号链接严格限制依赖访问范围:- 每个包只能访问其声明的依赖(位于
.pnpm
内隔离的子目录)。
效果:避免幽灵依赖,更符合预期。
- 每个包只能访问其声明的依赖(位于
5. Monorepo 支持
- pnpm:
原生支持 Monorepo,通过pnpm-workspace.yaml
配置。
所有子项目共享依赖到全局仓库,极大优化安装效率和磁盘占用。 - npm:
需借助lerna
等工具实现 Monorepo,依赖管理效率较低。
使用场景建议
- 推荐 pnpm:
- 磁盘空间有限(如 CI/CD 环境)。
- 项目依赖复杂或需严格隔离(避免幽灵依赖)。
- Monorepo 项目。
- 追求更快的安装速度。
- 继续用 npm:
- 项目简单且无磁盘压力。
- 历史项目迁移成本过高。
- 依赖某些 npm 特有的生态工具(但 pnpm 兼容大部分命令)。
命令兼容性
pnpm 设计上兼容 npm 常用命令:
pnpm run <script> # 等同于 npm run
pnpm test # 等同于 npm test
pnpm add <pkg> # 等同于 npm install <pkg>
迁移到 pnpm
- 删除现有
node_modules
和package-lock.json
。 - 安装 pnpm:
npm install -g pnpm
。 - 运行
pnpm install
(自动生成pnpm-lock.yaml
)。
⚠️ 注意:某些深依赖嵌套的项目可能需调整(如依赖未声明但访问了提升的包)。
总结:
pnpm 通过硬链接+符号链接机制,在保证依赖严格隔离的同时,显著节省磁盘空间并提升安装速度,尤其适合大型项目或 Monorepo。而 npm 作为 Node.js 原生包管理器,更适合简单场景或兼容性优先的项目。