• 一文读懂npm、yarn、pnpm直接的关系
  • 发布于 2个月前
  • 348 热度
    0 评论
npm<v3
node 依赖管理器的起源,在 npm 前两个版本中,依赖无限嵌套,各种重复,被调侃质量比黑洞还大。

yarn v1
yarn,代表 Yet Another Resource Negotiator。v1 版本现在被称为 yarn classic,当年拥有不少注目特性,DX(开发体验)↑ 安全 ↑ 安装速度 ↑,创新点:
1.依赖提升(hoisting)、依赖安装扁平化
2.引入 lock 文件
3.有一些 npm 没有的命令,例如更新依赖到最新版本
4.支持 monorepo
5.离线缓存
但是引入了两个问题:
1.NPM doppelgangers
2.Phantom dependencies
点击上面两个链接有详细解析,下面用中文总结几句。

依赖分身
依赖提升只能提升一个版本,其他版本依然安装在嵌套的 node_modules 中,会重复安装。所以如果项目里要求各种固定版本,依然是要安装一大堆重复的东西。

不过好消息是按照正常开发习惯和开发周期,下面这一大堆其实都只需要安装一次,以下是真实项目的 lock 文件的一部分:
lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@~4.17.0:
  version "4.17.21"
  resolved "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
  integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
安装了一大堆不同版本的 lodash 其实都可以使用 4.17.21,它符合所有版本要求。不确定版本要求的写法可以在 npm semver calculator 查询。

幽灵依赖
因为依赖提升这个特性,非直接依赖的包你也能直接使用,因为全都平铺在顶层 node_modules,这种可以依赖你没直接依赖的包的情况就是幽灵依赖。
以前开发一个库的时候就遇到这个问题,开发的时候默认引入了某个工具,开发时一切正常,但是打包出来别人用的时候缺了一个依赖。

npm>=v3
npm3 之后的版本向 yarn 学习:
1.跟进了 hoisting
2.跟进了 lock 文件
How npm3 Works 比较详细地解析了 npm3 的改进。

然而……该有的问题还是没解决。

pnpm
pnpm 使用软硬链接的巧妙结合同时解决以上两个问题。
1.所有依赖有一个全局仓库,被引用的会硬链(hard link)到项目
2.对于依赖之间的关系,使用软链(Symlink)链接,实现最短路径查询依赖
官网给出这样的例子:
node_modules
├── foo -> ./.pnpm/foo@1.0.0/node_modules/foo
└── .pnpm
    ├── bar@1.0.0
    │   └── node_modules
    │       ├── bar -> <store>/bar
    │       └── qar -> ../../qar@2.0.0/node_modules/qar
    ├── foo@1.0.0
    │   └── node_modules
    │       ├── foo -> <store>/foo
    │       ├── bar -> ../../bar@1.0.0/node_modules/bar
    │       └── qar -> ../../qar@2.0.0/node_modules/qar
    └── qar@2.0.0
        └── node_modules
            └── qar -> <store>/qar
带 <store>/ 的是硬,带 ../../ 的是软。即使依赖深度是 foo > bar > qar 依然可以保持这样的浅层结构。顶层只有一个 foo,没有幽灵依赖,项目用到的版本都在 .pnpm 平铺,不会造成依赖分身。

yarn>=v2
可以使用激进的 Plug’n’Play (PnP)。
Yarn generates a single .pnp.cjs file instead of the usual node_modules folder containing copies of various packages.

PnP 不用 node.js 自己去找依赖位置了,直接用 .pnp.cjs 告诉它所有依赖的准确位置,这个准确位置是一个中央仓库,所以也不需要把依赖复制到项目的 node_modules 里。PnP 出现之初有很多工具都不支持这种依赖安装方法,现在兼容性估计好很多了。但是只要 pnpm 的速度还能接受,个人认为还是直接用传统 node_modules 吧。

对比

很惊讶,根据 JavaScript package managers compared: npm, Yarn, or pnpm? 中的性能测试结果,npm v8.1.2 已经比 yarn v1.23.0 快了,首次安装的速度 pnpm 仍然拉开两位前辈一大截。打破常规的 PnP 自然是最快的,但是要在项目中使用它,还得看项目能不能兼容 PnP。(虽然 pnpm 的软硬链有些包也不支持)

用户评论