Dependency Hell介绍(依赖地狱)
nest mode(维护拓扑图)
上图就是依赖地狱的产生路径,在node_modules中,不同的package会有自己的依赖,并且会将依赖放置到自身的node_modules中
可以发现,虽然mod-a和 mod-d依赖了同一个mod-b的版本,但是mod-b却安装了两遍,如果你的应用了很多的第三方库,同时第三方库共同依赖了一些很基础的第三方库,如lodash,你会发现你的node_modules里充满了各种重复版本的lodash,造成了极大的空间浪费,也导致npm install很慢,这既是臭名昭著的node_modules hell。
这个地方可以看出两个问题:
- 相同的依赖,重复安装;
- hell情况,依赖路径很长很长
flat mode
原理:(npm3新增)
总的来说,就是利用向上递归查找依赖的特性,将一些公共依赖放在公共的node_module里(遇到新的包就把它放在第一级目录,后面如果遇到一级目录已经存在的包,会先判断版本,如果版本一样则忽略,否则会按照 npm2 的方式依次挂在依赖包目录下)
优点:
可以解决依赖地狱和重复下载依赖包的问题
解释上面的图:
Av1.0依赖Bv1.0,C和D都依赖的是Bv2.0,根据package.json的解析顺序,先走到A模块,发现它依赖B1.0,此时top root没有跟B相同版本的包,所以会把B1.0提升到top root,然后解析C,发现依赖的是B的2.0,找top root,发现已经有相同的包B,但是版本不一样,所以会放到C下面,如果当前C依赖的是B1.0,则不会继续下载,因为top root已经有B的1.0版本了,所以就做到,在一定程度下,防止重复包的出现
缺点:
- 同一层级下会出现不同版本的相同依赖包,而且会出现某一个包升级之后,包依赖关系变化(npm机制,更新包之后会重新更新结构),相同依赖包出现不同版本,大概率下是不会有问题的,因为版本冲突很少出现,但是如果不同版本的依赖包中,有修改全局的类型定义,则会出现问题,例如:@types/react,会造成命名冲突
- 破坏单例模式
- Phantom dependency(影子依赖)
- 把一个库使用了不属于其depdencies里的package称之为phantom depdencies
- 例如:直接使用某一个库内部的api、放到devDependency中,但是实际打包需要
对于以上问题,npm和yarn都没有很好的解决办法
semver
就是对npm包的语义化版本,也就是^、~这些符号,进行版本约束,防止某一次重新下载版本而导致不兼容的问题
lock
npm5+才有,不管是npm的lock还是yarn的lock,都可以帮助我们将项目中的package进行版本锁定
yarn和npm的lock:
- yarn.lock保证了所有第三方库和其依赖的版本号是锁定的。但是不包含任何的node_modules拓扑信息
- npm则保证了不同版本的确定性。包含拓扑信息,所以npm更优
这个时候,就出现了 pnpm