npm 如何处理依赖与依赖冲突

本文详细介绍了npm 2和3在处理依赖和冲突上的区别。npm 2采用嵌套方式导致深层依赖和资源重复,而npm 3尝试平铺依赖以减少冗余,但可能导致重复下载。npm 3的处理依赖顺序对目录结构有直接影响,可通过`npm dedupe`减少重复。安装顺序的变化可能造成目录结构不一致,但不影响应用运行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

详情见:
https://www.yuque.com/docs/share/d64606fd-0e7b-4423-b200-93bdbbb1c4e9?# 《npm 如何处理依赖与依赖冲突》

图片懒得复制了

npm2是如何处理依赖的?

假设有三个模块:A、B、C。A依赖B@1.0,C依赖B@2.0。
在这里插入图片描述

现在,我们创建一个应用,它同时以来A、C。
在这里插入图片描述

依赖地狱

在这里插入图片描述

npm2会这么处理:

此时的目录结构是这样的:

此时执行npm ls查看包的依赖关系是这样的:

问题

这么做带来很多问题

  1. 依赖树的层级非常深。如果需要定位某依赖的依赖,很难找到该依赖的文件所在(例如,如果想定位模块 E,就不得不先知道他在依赖树中的位置);
  2. 不同的依赖树分支里,可能有大量实际上是同样版本的依赖(例如,A 目录下的 C 和 B 目录下面的 C 如果版本一致,实际上完全一样);
  3. 安装时额外下载或拷贝了大量重复的资源,并且实际上也占用了大量的硬盘空间资源等(例如,C 模块在依赖目录中出现了两次);
  4. 安装速度慢,甚至因为目录层级太深导致文件路径太长的缘故,在 windows 系统下删除 node_modules 文件夹也可能失败!

npm3是如何处理依赖的?

npm3 解决依赖关系的方式与 npm2 不同。

npm2是以嵌套方式安装所有依赖项,但 npm3 试图减轻这种嵌套导致的深度和冗余。 npm3 通过把一些secondary依赖项和primary依赖项平铺在同一层级的方式来实现这一点。

主要的区别是:
● 目录结构中的位置不再代表着依赖项的类型(primary、secondary等)
● node_modules的目录结构取决于安装顺序

假设我们有一个模块 A,A 依赖 B。
现在,我们创建一个应用,它依赖A。

执行npm install, npm v3会把A、B以及他们的依赖都下载到/node_modules下平铺。在npm v2中,他们会被嵌套下载。

现在如果我们还需要依赖C,C依赖于B,但是依赖的是B@2.0。

因为B@1.0已经存在于顶层目录中了,所以我们不能把B@2.0也下载到顶层目录。npm v3此时会和npm v2一样的嵌套方式去处理新的依赖B@2.0,把它放在依赖它的目录下,也就是C@1.0下,如下图:

此时的目录结构是这样的:

此时执行npm ls查看包的依赖关系是这样的:

如果你只想看primary依赖,执行npm ls --depth=0

npm3是如何处理重复的?

接着上面,我们继续。如果我们现在要再下一个模块D,同时D依赖于B@2.0呢?

因为B@1.0已经存在于顶层目录中了,所以我们不能把B@2.0也下载到顶层目录。此时B@2.0会以嵌套的方式下载到模块D下面,尽管在C的目录下已经有了一个B@2.0了。

可以看出,如果一个secondary的依赖同时被两个primary依赖所依赖,但是这个secondary依赖并没有被下载到顶层目录中,那么它将会被分别下载到这两个primary依赖的目录下,这就出现了重复!

但是如果说这个secondary的依赖已经被下载到顶层目录中,那它将会在这两个primary依赖中共享,并不会出现重复下载!

比如说,我们现在还需要下载一个模块E,模块E和A一样,依赖于B@1.0。

因为B@1.0已经在顶层目录了,所以我们不需要重复下载它,只需要单纯的下载E,它会和A共享B。

现在的目录结构是这样的:
现在,如果我们A升级到了2.0,而A@2.0依赖的不再是B@1.0,而是B@2.0呢?会发生什么?

要解决这个问题,最关键的是要记住,下载顺序很重要!
即使说A@1.0是第一个被package,.json下载的(因为它是按字母顺序排序的),但当时你执行npm install后,就意味着A@2.0是最新下载的包!
当我们执行npm install mod-a@2 --save后,npm3会做一下的事情:
● 移除A@1.0
● 下载A@2.0
● 它会保留顶层的B@1.0,因为E@2.0还需要它
● 它会把B@2.0下载到A@2.0的目录下去,因为此时顶层有B@1.0在

最后,如果我们把E更新到v2.0,E@2.0依赖B@2.0,会发生什么?

npm3会做以下事情:
● 移除E@1.0
● 下载E@2.0
● 移除掉B@1.0,因为没有模块还需要依赖它
● 把B@2.0下载到顶层目录,因为顶层目录已经没有任何版本的B了

现在这样显然还不够理想,太多重复的B@2.0了。为了去掉重复,我们可以执行npm dedupe,这个命令会将所有依赖B@2.0的包的依赖重定向到顶层的B@2.0上,并且把它们下面的B@2.0移除掉。

npm3的不确定性

现在让我们跳回到之前一个例子中

此时你的package.json 是这样的
{
“name”: “example3”,
“version”: “1.0.0”,
“description”: “”,
“main”: “index.js”,
“scripts”: {
“test”: “echo “Error: no test specified” && exit 1”
},
“keywords”: [],
“author”: “”,
“license”: “ISC”,
“dependencies”: {
“mod-a”: “^1.0.0”,
“mod-c”: “^1.0.0”,
“mod-d”: “^1.0.0”,
“mod-e”: “^1.0.0”
}
}
现在如果你执行npm install mod-a@2 --save,我们的目录结构将会变成这样:

而你的package.json也更新了,此时是这样的:
{
“name”: “example3”,
“version”: “1.0.0”,
“description”: “”,
“main”: “index.js”,
“scripts”: {
“test”: “echo “Error: no test specified” && exit 1”
},
“keywords”: [],
“author”: “”,
“license”: “ISC”,
“dependencies”: {
“mod-a”: “^2.0.0”,
“mod-c”: “^1.0.0”,
“mod-d”: “^1.0.0”,
“mod-e”: “^1.0.0”
}
}
而如果此时你的同事小高拿到最新的项目,执行npm install,得到的目录将会是这样的

what?竟然和你刚才执行的结果完全不同?此时只要记住,下载顺序很重要!
因为小高拿到新的项目,执行npm install,此时的下载顺序是由package.json中的字母顺序决定的,第一个下载的就是A@2.0,随即把B2.0也到了顶层。

所以npm3并不能总是保证每个人的目录结构是一样,但是这并不影响你的应用!一切都可以正常进行!

如果你实在是想让node_modules的目录结构保持一致的话,你可以先把node_modules都删掉,然后再执行npm install,因为此时的顺序永远是package.json中的字母顺序!

参考链接

  1. https://npm.github.io/how-npm-works-docs/npm2/how-npm2-works.html
  2. http://aprilandjan.github.io/npm/2019/08/02/how-npm-handles-dependency-version-conflict/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值