npm
因为 node 的依赖包存在互相依赖关系,如 A 依赖 B,B 依赖 C,因此在本地依赖包的管理可以分为树状结构和扁平状结构。
npm2 和 npm3 的依赖结构树不同:
npm2 是树状依赖结构,这在 windows 系统下可能会存在问题,因为有很多程序无法处理超过260个字符的文件路径名。并且这种方式也会出现同一版本的 npm 包被重复安装的问题。
node_modules
- package-A
-- node_modules
--- package-B
----- node_modules
------ package-C
-------- some-really-really-really-long-file-name-in-package-c.js
npm3 采用扁平化的方式管理依赖包,不过这种方式的致命缺点是,npm必须首先遍历所有的项目依赖关系,然后再决定如何生成扁平的node_modules目录结构。npm必须为所有使用到的模块构建一个完整的依赖关系树,这是一个耗时的操作,是npm安装速度慢的一个很重要的原因。(扁平化算法比较耗时,且依赖结构存在不确定性)
node_modules
- package-A
- package-B
- package-C
-- some-file-name-in-package-c.js
而且 npm 还有一个问题,同一个项目,安装的时候无法保持一致性。如下。
"5.0.3",
"~5.0.3", # 安装 5.0.x 的最新版本
"^5.0.3" # 安装 5.x.x 的最新版本
Yarn
yarn 和 npm 相比,有以下几个显著特点:
- 并行安装。npm 会按照队列安装每一个 package,必须要等前一个 package 安装完成才能安装后一个 package,而 yarn 是同步并行执行任务,提高了速度。
- 离线模式。如果之前安装过一个 npm 包,会在本地形成缓存,yarn 会优先从缓存获取 npm 包。
- 安装版本统一。和 package.json 不同,yarn.lock 会锁定准确的版本号。npm 5 以后提出了 package-lock.json 解决了类似的问题。
- 更简洁的输出。
- 多注册来源处理。所有的依赖包,不管他被不同的库间接关联引用多少次,安装这个包时,只会从一个注册来源去装,要么是 npm 要么是 bower, 防止出现混乱不一致。
更好的语义化。 yarn改变了一些npm命令的名称,比如 yarn add/remove,感觉上比 npm 原本的 install/uninstall 要更清晰。
pnpm
pnpm 主要有以下几个特点:
速度更快。相比于 Yarn PnP,pnpm 的安装速度更快
- 高效的利用磁盘空间。pnpm 内部使用基于内容寻址的文件系统来存储磁盘上所有的文件。其特点为:
- 不会重复安装同一个包。用 npm/yarn 的时候,如果 100 个项目都依赖 lodash,那么 lodash 很可能就被安装了 100 次,磁盘中就有 100 个地方写入了这部分代码。但在使用 pnpm 只会安装一次,磁盘中只有一个地方写入,后面再次使用都会直接使用 hardlink。
- 即使一个包的不同版本,pnpm 也会极大程度地复用之前版本的代码。举个例子,比如 lodash 有 100 个文件,更新版本之后多了一个文件,那么磁盘当中并不会重新写入 101 个文件,而是保留原来的 100 个文件的 hardlink,仅仅写入那一个新增的文件。
- 支持 monorepo。
- 安全性高。如果 A 依赖 B, B 依赖 C,那么 A 当中是可以直接使用 C 的,但问题是 A 当中并没有声明 C 这个依赖。因此会出现这种非法访问的情况。 npm / yarn 无法解决这种情况,而 pnpm 独有的依赖管理方式解决了这个问题。