【笔记】包管理工具pnpm整理
Feb 19, 2022笔记工程nodejs包管理工具 pnpm 整理
官网:https://pnpm.io/,github:https://github.com/pnpm/pnpm
其实 pnpm 官网从使用、特性、原理社区等建设得都很细致,真正打算使用的话建议通读下官网。
特点
Fast, disk space efficient package manager
pnpm 也是一款包管理工具,这点与 npm/yarn/cnpm/tnpm 一样,目前主流版本为6.x
,7.0
也已开始。
其实 pnpm 是一个挺早的项目,在 21 年才在国内火了起来。
截止目前(2022.02.19)来看,该项目已上升至 15k 的 star,并且像 vue3 等一些著名项目也开始使用了 pnpm:
从源码来看,pnpm 是一个高浓度 TypeScript 项目(目前代码中 99%+都是 ts),所以源码方面可以放心食用。
优点
直接拿官网的宣称:pnpm 是一款快速、节省磁盘空间的包管理器。
- 快速:pnpm 比替代方案(yarn/npm 等)快 2 倍
- 高效:node_modules 中的文件是从一个单一的可内容寻址的存储中链接过来的
- 支持 monorepos:pnpm 内置支持了单仓多包
- 严格:pnpm 默认创建了一个非平铺的 node_modules,因此代码无法访问任意包
pnpm 本质上就是一个包管理器,这一点跟 npm/yarn 没有区别,核心优势就是快以及磁盘利用得好。其他优势也可以在作者 blog 中查看:https://www.kochan.io/
以下是一些比较数据,也可以在官网上看到:
node_modules
根据核心优势,就需要大致了解下 node_modules 的安装和依赖模式:
Nested installation(嵌套安装): 在
npm@3
之前,node_modules
结构是干净、可预测的,因为node_modules
中的每个依赖项都有自己的node_modules
文件夹,在package.json
中指定了所有依赖项。结构就像这样:1
2
3
4
5
6
7
8
9
10
11
12
13node_modules
- package A
- packageX 1.0
- packageY 1.0
- package B
- packageX 2.0
- packageY 2.0
- package C
- packageX 1.0
- packageY 2.0
- package D
- packageX 2.0
- packageY 1.0Flat installation(扁平安装):
npm@3 / yarn
,扁平化结构,多个版本的包只能有一个被提升上来(hoist 机制),优势就是避免了一些重复,其余版本的包会嵌套安装到各自的依赖当中(类似 npm2 的结构),至于哪个版本的包被提升,依赖于包的安装顺序。结构就像这样:1
2
3
4
5
6
7
8
9
10
11
12
13node_modules
- package X => 1.0 版本
- package Y => 1.0 版本
- package A
- package B
- packageX 2.0
- packageY 2.0
- package C
- packageY 2.0
- package D
- packageX 2.0
```
pnpm 既然改了依赖方式,那么就可以看当前模式的主要问题:
- 重复安装,如上所示的 packageX 2.0 和 packageY 2.0 被重复安装多次,从而造成 npm 和 yarn 的性能一些性能损失。这种场景在 monorepo 多包场景下尤其明显,另外扁平化的算法实现也相当复杂,改动成本很高。
- Phantom dependencies,幽灵依赖或幻影依赖,即某个包没有在
package.json
被依赖,但是用户却能够引用到这个包。
pnpm 的改变:
Net 网状 + Flat 平铺 的
node_modules
结构- 虚拟存储目录:
.pnpm
, 以平铺的形式储存着所有的包,正常的包都可以在这种命名模式的文件夹中被找到(peerDep 例外):
1
2
3.pnpm/<organization-name>+<package-name>@<version>/node_modules/<name>
// 组织名(若无会省略)+包名@版本号/node_modules/名称(项目名称)该目录通过
<package-name>@<version>
来实现相同模块不同版本之间隔离和复用,由于它只会根据项目中的依赖生成,并不存在提升,所以不存在幽灵依赖。- 虚拟存储目录:
结构就像这样:
1 | node_modules |
如官网描述:
那么它如何跟文件资源进行关联的呢?答案是 Store + Links,可以看官网介绍《Symlinked node_modules
structure》或掘金上这篇易于理解的文章《pnpm 的破解之道:网状 + 平铺的 node_modules 结构》
根目录下的 node_modules
下面不再是眼花缭乱的依赖,而是跟 package.json
声明的依赖基本保持一致。即使 pnpm 内部会有一些包会设置依赖提升,会被提升到根目录 node_modules
当中,但整体上,根目录的 node_modules
比以前还是清晰和规范了许多。
pnpm 这种依赖管理的方式也巧妙地规避了非法访问依赖的问题,pnpm 不允许安装 package.json
中没有包含的包,也就是说只要一个包未在 package.json
中声明依赖,那么在项目中是无法访问的。
在此背景情况下,据说 yarn 也计划开始存储模式的优化。
缺点
在 node_modules
的依赖模式改变下,因为软链的关系必然也会导致一些问题:
- 1.子依赖提升到同级的目录结构的兼容性问题,类似 Egg、Webpack 的插件加载逻辑,在用到相对路径的地方,需要去适配;
- 2.软链在不同操作系统的实现不太一样,且在非 SSD 的硬盘上,还是会有一定的磁盘 io 损耗的。
- 3.多包某种情况下,依赖出现两次:可见官网说明https://pnpm.io/how-peers-are-resolved
其余大部分问题可在官网 faq中找到,也可以看 github 上一些 issue 反馈,包括现在的问题:
使用
极为简单
安装
1 | npm i -g pnpm |
使用
与 yarn 类似:
1 | pnpm add xxx # 安装xxx到dependencies |
pnpm-workspace.yaml
此文件定义了工作空间的根目录,并能够使您从工作空间中包含 / 排除目录 。 默认情况下,包含所有子目录。
如:
1 | # pnpm-workspace.yaml |
其他如钩子、别名等不太常用的功能查询官网https://pnpm.io/zh/motivation。
最后
新的工具必然带来性能/空间上的提升,但在业务使用中我们需要注意它的隐患,避免乐观主义陷阱。
Author
My name is Micheal Wayne and this is my blog.
I am a front-end software engineer.
Contact: michealwayne@163.com