首先 pnpm 和 npm yarn 一样是包管理工具,他解决了npm 和 yarn 存在的一些问题
* npm@3之前每个依赖都是一层嵌套一层的,每个依赖里都有node_modules 用来存放依赖所需的依赖包
* 导致重复下载的依赖包很多,一层层嵌套,嵌套很深,模块实例之间无法共享
之后的 npm@3 和 yarn 因为上述问题从而使用扁平化结构管理依赖
node_modules下所有的依赖都会平铺到同一层级。由于 require 寻找包的机制,如果 A 和 C 都依赖了 B,那么 A 和 C 在自己的
node_modules 中未找到依赖 C 的时候会向上寻找,并最终在与他们同级的node\_modules 中找到依赖包
C。这样就不会出现重复下载的情况,而且依赖层级嵌套也不会太深,因为没有重复的下载,所有的 A 和 C
都会寻找并依赖于同一个B包,自然也就解决了实例无法共享数据的问题。
虽然解决了问题,但是也带来了新问题
* 依赖结构的不确定性
* 需要借助lock文件,保证no de_modules
结构稳定性。否则出现同一层级相同包,不同版本的话,会根据安装的顺序,给一个版本提升到公共node_modules中,而另一个版本则会继续以嵌套node_modules的形式嵌套
* 扁平化算法的复杂度增加
* 项目中仍然可以非法访问没有声明过的依赖包(幽灵依赖)
* 虽然是依赖中依赖的包,没有在本身项目的package.json
中没有声明,但是由于扁平化处理,我们不仅仅发现多出很多没有声明在package.json
中的包外,我们还可以直接在项目中引用这类没有被声明的包,这就造成了幽灵依赖
以上就是 npm. 和 yarn 的一些问题,那么pnpm 如何解决这类问题呢
pnpm 的优势
* 包安装速度非常快
* 磁盘利用效率非常高(基于内容寻址)
* 不会重复安装同一个包。用 npm/yarn 的时候,如果 100 个项目都依赖 lodash,那么 lodash 很可能就被安装了 100
次,磁盘中就有 100 个地方写入了这部分代码。但在使用 pnpm 只会安装一次,磁盘中只有一个地方写入,后面再次使用都会直接使用hardlink
* 即使一个包的不同版本,pnpm 也会极大程度地复用之前版本的代码。举个例子,比如 lodash 有 100
个文件,更新版本之后多了一个文件,那么磁盘当中并不会重新写入 101 个文件,而是保留原来的 100 个文件的hardlink,仅仅写入那 `一个新增的文件
* 支持monorepo
* pnpm 与 npm/yarn 另外一个很大的不同就是支持了 monorepo,pnpm 内置了对 monorepo
的支持,只需在工作空间的根目录创建 pnpm-workspace.yaml 和 .npmrc 配置文件,同时还支持多种配置,相比较 lerna 和 yarn
workspace,pnpm 解决 monorepo 的同时,也解决了传统方案引入的问题。
pnpm依赖的管理
pnpm 使用的是 npm version 2.x 类似的嵌套结构,同时使用 .pnpm 以平铺的形式储存着所有的包,然后使用 Store + Links
和文件资源进行关联。简单地说 pnpm 把会包下载到一个公共目录,如果某个依赖在 sotre 目录中存在了话,那么就会直接从 store 目录里面去
hard-link,避免了二次安装带来的时间消耗,如果依赖在 store 目录里面不存在的话,就会去下载一次。通过 Store + hard link
的方式,使得项目中不存在 NPM 依赖地狱问题,从而完美解决了 npm3+ 和 yarn 中的包重复问题
* 软链接 和 硬链接 机制
* pnpm 是通过 hardlink 在全局里面搞个 store 目录来存储 node_modules 依赖里面的 hardlink
地址,然后在引用依赖的时候则是通过symlink 去找到对应虚拟磁盘目录下(.pnpm 目录)的依赖地址
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3ntHPliC-1686748158315)(/Users/wangyongjie/Library/Application
Support/typora-user-images/image-20230614210025958.png)]
以上是A,B两个依赖使用pnpm安装后的 node_modules
结构,可以看到首先会去全局的store中查找A,B两个依赖是否存在,不存在则下载存入这个公共store,
然后对声明的A,B两个依赖通过软连接.pnpm文件夹下的各自依赖A,B,进行关联,而.pnpm则以平铺形式存储所有包,而真正包 则会硬链接形式链接到项目