前言
npm/yarn 依赖管理
早期的
使用早期的 npm1/2 安装依赖,node_modules 文件夹会以递归的形式呈现,严格按照 package.json 结构以及次级依赖的 package.json 结构将依赖安装到它们各自的 node_modules 中,直到次级依赖不再依赖其它模块。
就像下面这样,foo 依赖 bar 作为次级依赖,bar 会安装到 foo 的 node_modules 里面:
node_modules
└─ foo
├─ index.js
├─ package.json
└─ node_modules
└─ bar
├─ index.js
└─ package.json
假设项目的中的两个依赖同时依赖了相同的次级依赖,那么它们二者的次级依赖将会被重复安装。
node_modules
├─ foo1
│ ├─ index.js
│ ├─ package.json
│ └─ node_modules
│ └─ bar
│ ├─ index.js
│ └─ package.json
└─ foo2
├─ index.js
├─ package.json
└─ node_modules
└─ bar
├─ index.js
└─ package.json
这只是简单的例子,在真实的开发场景中其问题还会更加恶劣:
- 依赖层级太深,会导致文件路径过长(在 Windows 系统下会出现一些问题)
- 重复的包被安装,导致 node_modules 文件体积巨大,占用过多的磁盘空间
转折点
自 npm``3/yarn 开始,相比 npm1/2 项目依赖管理的方式有了很大的改变,不再是以往的“嵌套式”而是采用了“扁平化”方式去管理项目依赖。
这里继续拿上面的例子,foo1 和 foo2 都依赖了 bar,依赖安装后呈现的是下面的这种扁平化目录:
node_modules
├─ bar
│ ├─ index.js
│ └─ package.json
├─ foo1
│ ├─ index.js
│ └─ package.json
└─ foo2
├─ index.js
└─ package.json
扁平化的目录的确解决了上一小节暴露的一些问题,同时也暴露了新的问题:
- Phantom dependencies
称为“幽灵依赖”,指的是在项目内引用未在 package.json 中定义的包。这个问题在 npm3 展现,因为早期的树形结构导致了依赖冗余和路径过深的问题,npm3 之后采用扁平化的结构,一些第三方包的次级依赖提升到了与第三方包同级。
一旦出现幽灵依赖的问题,可能会导致意想不到的错误,所以一定要正视:
- 不兼容的版本(例如某一个 api 进行了重大更新)
- 有可能会丢失依赖(某依赖不再依赖呈现在我们项目中的幽灵依赖)
// bar 就属于是幽灵依赖,因为它是属于 foo1、foo2 的次级依赖。
import bar from 'bar';
- NPM doppelgangers
称为“分身依赖”,在 monorepo 项目中非常常见,项目中依赖的第三方包以及第三方包所依赖的同名包都会被重复安装。
常见的问题:
- 项目打包会将这些“重身”的依赖都进行打包,增加产物体积
- 无法共享库实例,引用的得到的是两个独立的实例
- 重复 TypeScript 类型,可能会造成类型冲突
在实际开发中也会出现这样的情景,假设 foo1、foo2、依赖 lodash@3.6.0,bar 依赖 lodash@4.5.0,这时候会造成依赖冲突,解决冲突的方式会将对应的冲突包放到对应依赖目录的 node_mudules 中,类似下面结构:
node_modules
├─ lodash@4.5.0
│ ├─ index.js
│ └─ package.json
├─ foo1
│ ├─ index.js
│ ├─ package.json
│ └─ node_modules
│ └─ lodash@3.6.0
│ ├─ index.js
│ └─ package.json
├─ foo2
│ ├─ index.js
│ ├─ package.json
│ └─ node_modules
│ └─ lodash@3.6.0
│ ├─ index.js
│ └─ package.json
└─ bar
├─ index.js
└─ package.json
这时候你可能会发现一个问题,foo1、boo2 node_modules 下都有重复且版本相同的 lodash@3.6.0,这个问题就是我们正在所说的“分身依赖”的问题。
可能你还会有另外一个疑惑,什么不扁平 lodash@3.6.0,这样能减少一份所占用的空间,还能够解决“分身依赖”的问题。这是因为具体是扁平谁这是根据依赖的顺序决定的。因为开发者不关注依赖的顺序,所以存在很大的不确定性。
结论
- 扁平化的 node_modules 结构允许访问没有在 package.json 中声明的依赖。
- 安装效率低,大量依赖被重复安装,磁盘空间占用高。
- 多个 FE 项目之间已经安装过的的包不能共享,每次都是重新安装。
评论区