包管理器安装机制和幽灵依赖问题

1,323 阅读4分钟

大家好我是堂主,今天水一篇文章,探索下主流包管理工具的依赖安装机制和问题。

(同步发布在我的公众号:咪仔和汤圆,欢迎关注~)

到目前流行的三种安装和管理工具:npmyarnpnpm,通过一个例子来进行演示,创建一个pm-demo工程,工程里面包含两个dependencies

"dependencies": {
    "@awefeng/state-machine": "1.1.2",
    "@awefeng/npm-template": "1.0.1"
 },

其中,state-machine是用npm-template作为模版写的,这两个包的有相同的依赖。

npm1.0+的安装机制

npm1.0+版本采用的是树形结构进行安装,没有对依赖进行优化,使用到的库的依赖里面如果有相同的库,则会重复下载:

pm-demo
  ├─ @awefeng/state-machine
  |  ├─ @babel/runtime-corejs3
  └─ @awefeng/npm-template
     ├─ @babel/runtime-corejs3

npm1.0+.png

可以从图上看到,重复安装了相同版本的@babel/runtime-corejs3,优点就是结构清晰,缺点很明显就是重复下载、占用过大、依赖包层级太深(路径过长在window系统下会出问题,详见参考[1])。

npm3.0+、yarn

npm3.0+以后采用了和yarn一样的机制,将所有依赖包都进行扁平化处理,即将所有的依赖包都放在工程根目录下的node_modules。如果有相同库但是版本要求不一样,则是将某一个版本的先放到根目录的node_modules下,遇见库的其他版本的时候,再放到对应依赖的node_modules下。

pm-demo
├─ @awefeng
|  ├─ state-machine
|  ├─ npm-template
└─ @babel/runtime-corejs3

npm3+.png

yarn.png

上面两张图分别是在npm8yarn1.22.x安装依赖生成的。

针对需要安装不同版本的情况,很好验证,我们在工程根目录里添加低于7.18.3@babel/runtime-corejs3(吐槽这个包最低版本都是7.x,本来想安装个6.x进验证),看一下怎么安装的:

"dependencies": {
    "@awefeng/state-machine": "1.1.2",
    "@awefeng/npm-template": "1.0.1",
    "@babel/runtime-corejs3": "7.4.5"
},

unsame.png

没错,安装了3次,7.4.5的安装了一次,7.21.0的分别在state-machinenpm-template下又各安装了一次,所以这种依赖安装机制只能说是优化了,但是没完全优化。

还引入了新的问题(下一节讨论),并且也没有很好的解决window下路径过长的问题:

window路径过程的问题.png

这里有个小的tip就是怎么确定哪一个版本放在根目录的node_modules扁平化?

幽灵依赖

npm3.0+yarn的依赖安装机制引入了“幽灵依赖”问题,在项目中package.json本来没有引入某一个包,由于扁平化的原因,在代码中却能使用,比如我们项目没有引入core-js-pure,却能在代码中使用:

image.png

“幽灵依赖”问题会在项目某些包升级或者某些依赖包更新等情况下出现严重问题(详见参考[2])。

pnpm

如何很好的解决路径过长、重复安装、幽灵依赖的问题?

pnpm给出了答案:通过硬链接和软链接(详见参考[3],软链接也叫符号链接)来连接依赖,将依赖统一存储在一个store里并且进行了相同依赖不同版本的文件复用。

pnpm安装依赖大致有以下几个步骤:

  1. 解析依赖树并下载依赖到全局store(可以通过pnpm store path查看)
  2. 计算出根目录的文件结构
  3. 链接依赖

第一步解析下载没什么好说的,其中要注意的就是相同库的不同版本之间相同文件的复用,以及pnpm中解析,下载两个阶段是并行的(注意链接阶段不是,链接阶段至少是要确定好了目录以后):

image.png

第二步是计算出文件结构,pnpm分析出具体需要的依赖树和文件复用,pnpm首先会将node_modules初始化为.pnpm和直接依赖项

第三步在.pnpm中通过硬链接连接所有的依赖项,然后和.pnpm同级下只出现项目的直接依赖,直接依赖通过软链接连接到.pnpm下。

更详细的解释查看官网阐述的基于符号链接的node_modules(详见参考[4])。

同样举例我们的demo,生存的node_modules文件目录如下:

image.png

end

下一篇探索下workspace机制和在有peer依赖下的安装机制。 参考文章:

[1] Window最大路径长度限制:learn.microsoft.com/zh-cn/windo…

[2] 幽灵依赖:www.kochan.io/nodejs/pnpm…

[3] 硬链接和符号链接:abcfy2.gitbooks.io/linux_basic…

[4] 基于符号链接的node_modules结构:pnpm.io/symlinked-n…