pnpm相比npm和yarn的优势有哪些?
- 安装速度更快
- 占用磁盘空间更小
- 可以避免幽灵依赖
- 版本严格依赖
为什么占用空间小,安装更快呢?
早在npm v2早期,我们安装的依赖都是把各个包的依赖安装到自己的 node_modules中,例如有c包依赖b包依赖a包 就会出现层层依赖的关系如下
c
node_modules
b@0.0.1
node_modules
a@0.0.1
到了npm v3版本,和 yarn 一样使用了扁平的依赖方式最终 b 和 a 的包都会被打平到一个 node_modules 中,为什么要打平提出来呢,这样如果 c包再依赖了 b@0.0.1 就不会重复安装多个b包了。这样也节省空间了。
但是举个例子:
- 如果C包引用了B包的1.0.0的版本,D包引用了B包的1.0.5的版本,那么项目是安装了几个B包的版本呢,并且在使用的时候分别使用的哪个版本运行呢。
假设项目依赖关系如下:
项目 ├── C (依赖 B@1.0.0)
└── D (依赖 B@1.0.5)
npm/Yarn (扁平化策略)安装后如下
node_modules
├── B (版本 1.0.5)
├── C
│ └── node_modules
│ └── B (版本 1.0.0)
└── D
- npm/Yarn 会尝试将依赖"提升"到顶层
- 由于版本冲突,只能将一个版本放在顶层
这样空间虽然减少了但是又会存在一个幽灵依赖的问题,比如:我项目里只依赖了C包和D包,但是我可以直接 require('B') 因为b包再扁平化的 node_modules 中是可以正常访问的。
针对上面的问题,pnpm做了一些优化 pnpm使用严格且正确的依赖解析
pnpm (严格隔离策略)
安装结构:
node_modules
├── .pnpm
│ ├── b@1.0.0
│ │ └── node_modules
│ │ └── b -> <全局store中的B@1.0.0>
│ └── b@1.0.5
│ └── node_modules
│ └── b -> <全局store中的B@1.0.5>
├── c -> ./.pnpm/c@版本/node_modules/c
└── d -> ./.pnpm/d@版本/node_modules/d
安装逻辑:
- pnpm 维护一个全局 store,存储所有包的所有版本
- 每个依赖都严格使用声明的版本
- 通过硬链接和符号链接引用全局 store 中的包
结果: 全局 store 中会有 两个版本 的 B 包,项目通过链接引用它们
因为使用文件硬连接的方式,把所有的依赖都安装到了全局的store中,各个包都连接到store中,如果有重复的包,就不会被重复下载,只需要连接去过就可以,在我们现代项目多git的仓库中非常实用
-
- 安装速度快
- 是因为如果store中已经有了 类似
loadsh这样的包后,下次安装依赖的时候就只是link一下,当然快
-
- 占用空间小
- 是因为所有git仓库都共用全局的store,都是通过link的方式连接去过,所以相同的
loadsh包的相同版本只会安装一次
-
- 可以避免幽灵依赖
- 看上面的目录结构不全是打平的node_modules, 而是每个包有自己依赖的版本,都放到了
.pnpm下,这样如果B包没有被依赖的话,直接 require 是 访问不到了的