在
pnpm没出现之前,大家最常用的包管理工具有两个npm、yarn,那为什么会出现pnpm呢,以及pnpm的出现到底解决了哪些问题?
npm
1、npm 的 node_modules 采用了嵌套结构,包的依赖被直接下载在当前目录下,形成依赖地狱,最关键的是windows上支持的文件路径最长是 260 多个字符。
2、同一个版本的包文件,有可能被不同的包引入,造成重复下载,从而浪费了大量的磁盘空间。
npm2.x 目录结构
node_modules
├── moduleA@1.0.0
│ └── node_modules
│ └── moduleC@1.0.0
└── moduleB@1.0.0
└── node_modules
└── moduleC@1.0.0
└── node_modules
└── moduleD@1.0.0
从上面的目录结构我们能看到,多层的嵌套结构,以及 moduleC@1.0.0 被重复下载了两次。
npm 官方也很快发现了这个问题, 并在 npm3.x 中做了改进。
npm3.x 中 node_modules 采用了扁平化的结构。将所有的子依赖都提升到跟主依赖平级的目录下。也就是 hoist 机制。
npm3.x 目录结构
── node_modules
├── moduleA
├── moduleB
└── moduleC
这在很大程度上解决了地狱依赖的问题,以及磁盘空间的浪费问题,但同时又带来了其他问题
1、幽灵依赖
在package.json中没有声明依赖的包,因为其他包依赖,被下载在 node_modules 根目录下,导致也可以直接在项目中直接引用
2、下载文件的不确定性
同一份 package.json,下载出来的 node_modules 可能不一致的问题。
也就是说两个依赖包同时依赖的C文件的不同版本,但只有一个版本才能被 hoist,那么到底应该提升哪个版本呢,答案是看安装时的顺序。
npm 5.x 为此新增了 package-lock.json 文件,强制锁定依赖文件的版本号,以及不同版本如何 hoist 的问题。
这样就确保了同一份 package.json 下载出来的 node_modules 目录结构一致、但 幽灵依赖 问题仍然没有得到解决
yarn
yarn 的出现,弥补了 npm2.x 的众多缺陷。
1、采用扁平化的方式管理 node_modules,并且首次提出了下载缓存的概念,下载速度大幅提升。
2、yarn.lock
yarn.lock 文件记录了包文件的版本号,以及它们之间的依赖关系,确保同一个项目生成同样的 node_modules
但是
yarn跟npm一样,都没有解决幽灵依赖的问题。
pnpm
pnpm 在 2017 年发布,它的出现,解决了 npm、yarn 长期以来存在的性能、安全访问等问题。
我们来看看官方的介绍:
Fast, disk space efficient package manager
译为:一个快速的,高效的磁盘空间利用的包管理器。
我们执行下 pnpm install,看下输出内容
Packages are hard linked from the content-addressable store to the virtual store
Content-addressable store is at: {home dir}/Library/pnpm/store/v3
Virtual store is at: node_modules/.pnpm
Content-addressable store(CAS),是一种存储信息的方式,根据内容进行检索信息的存储方式。
Virtual store(虚拟存储),指向存储的链接的目录,所有直接和间接依赖项都链接到此目录中,项目当中的.pnpm目录
pnpm install完成后,我们再看下 node_modules 的目录结构
node_modules
.pnpm
<package-name>@<version>/node_modules/<package-name>
react
eslint
...
我们可以发现,只有项目中真正依赖的包,才会软链在node_modules的目录下,其他所有的子依赖,以<package-name>@<version>的方式平铺在.pnpm目录下,因为没有依赖提升的概念,所以没有 幽灵依赖 这个问题
那 pnpm 是如何处理包之间的依赖关系呢? Store + Link
1、Store
pnpm 默认会将所有依赖包下载到系统{home dir}/Library/pnpm/store/v3中,系统中相同文件只会存在一份。
2、硬链接
pnpm install 的时候会先看Store中是不是存在该文件,如果存在,则会在.pnpm下新创建一个该文件的硬链接,hard link 到 Store 中对应的文件,而不是重新下载。下载速度、磁盘的利用率都得到大幅提升。
3、软链接
项目中依赖文件以及子依赖文件则是通过软链接的方式联系起来,所有的软链都是从 .pnpm <package-name>@<version>/node_modules/<package-name> 目录下链接过来
可以配合官方的图片具体看一下软硬链接的例子
总结
npm、yarn 存在的 幽灵依赖、下载速度慢、占用磁盘空间 等问题一直没有解决
pnpm 通过CAS的方式管理文件,通过软硬链接的方式来管理包与包之间的依赖。它的出现,解决了包管理器一直存在的痛点问题从而脱颖而出。