在前端开发里,包管理器是项目的后勤保障,影响着项目稳定与开发效率。早期 npm 凭借与 Node.js 绑定成首选,后因项目变大暴露短板。Yarn 凭性能与依赖处理优势崛起,如今 pnpm 又带来新方案。下面一起探索三者发展、特点与适用场景。
npm
npm v3之前
2011年7月,npm发布了1.0版本。当时的node_modules文件夹是什么样子呢?
node_modules
└─ foo
├─ index.js
├─ package.json
└─ node_modules
└─ bar
├─ index.js
└─ package.json
存在的问题
此时期主要是采用简单的递归依赖方法,最后形成高度嵌套的依赖树。然后就会造成如下问题:
- 依赖地狱 :重复依赖嵌套地狱,空间资源浪费,node_modules文件夹体积过大。
- 嵌套层级过深,解析慢,下载安装慢:高度嵌套的依赖树,解析慢,按照队列下载,这就会导致同一个时刻,只有一个模块在下载、解析、安装。
npm V3
为了解决上述问题,npm团队认真思考了node_modules的结构,并提出了扁平化的策略,就是把嵌套过深的层级,通过扁平化的方式,将依赖包进行提升,使嵌套层级尽可能的变少。node_modules的结构如下:
node_modules
├─ foo
| ├─ index.js
| └─ package.json
└─ bar
├─ index.js
└─ package.json
存在的问题
- 没有彻底解决重复安装的问题。
- 存在幽灵依赖问题,比如在安装时把c@1.0.0提升到了顶层,但是在package.json中并没有声明,项目中照样可以引用c@1.0.0。
- 不支持离线缓存模式,安装速度慢。
- 依赖分身:(同一依赖的多个版本会嵌套安装)
npm V5
学习yarn引入锁机制,通过package-lock.json 文件来锁定依赖的版本,确保在不同环境下安装的依赖版本一致。
yarn
2016 诞生了,此时 npm 处于 v3 时期,其实当时 yarn 解决的问题基本就是 npm v5 解决的问题,包括使用 yarn.lock 等机制,锁定版本依赖,实现并发网络请求,最大化网络资源利用率,其次还有利用缓存机制,实现了离线模式。
-
确定性安装:通过
yarn.lock文件精确锁定所有依赖的版本和来源。 -
并行安装:极大提升安装速度。
-
离线模式:利用全局缓存实现离线安装。
-
依赖树:类似 npm,也是扁平的,同样存在幽灵依赖和依赖分身问题。
pnpm
pnpm为什么被称为最先进的包管理工具?
特点&优点:
-
内容寻址存储,不会重复安装,节省磁盘空间。
-
- npm 和 Yarn 在处理依赖时,为了避免嵌套过深,会采用扁平的
node_modules结构,这可能导致依赖冲突和安全隐患。pnpm 采用嵌套的node_modules结构,每个包都有自己独立的依赖副本,确保了依赖的隔离性。
- npm 和 Yarn 在处理依赖时,为了避免嵌套过深,会采用扁平的
-
硬链接与符号链接结合
- pnpm 使用内容可寻址存储(CAS)来管理所有包文件。当安装一个依赖包时,pnpm 会将包文件存储在全局存储中,后续项目再次使用相同版本的包时,会通过硬链接指向全局存储中的文件,而不是重新下载。硬链接在文件系统中是多个文件名指向同一个物理文件,不额外占用磁盘空间。
- 对于项目中的依赖结构,pnpm 使用符号链接(软链接)来构建依赖树,将全局存储中的包链接到项目的
node_modules目录下。这种方式既保证了项目依赖的正确引用,又极大地节省了磁盘空间。
-
并行解析、安装处理,安装速度快。
-
默认支持工作空间(workspace)。