前几节中提到的,你的node_modules目录结果和依赖树结果是由安装顺序决定的。
如果你,或者你的开发团队使用package.json,并且使用npm install命令行去添加包的话,那么你的node_modules目录结构是可能跟你同事的node_modules目录结构不一样的,跟你的工作台、测试环境或者生产环境,也可能不一样。
简而言之就是npm3安装依赖的时候,并不是以一种确定的行为安装的。
读到这你或许有些惊讶,不过在这一小节中我将解释为什么会发生这种情况,并且让你了解到这种情况对你的应用是没有什么问题的。如果你还担心的胡啊,那么我还会给出一些方法让你重建一个一致的node_modules目录。
回到我们前面的例子来。
我们的应用依赖于ACDE模块,同时AE依赖于B V1.0,而CD依赖于B V2.0。
这种情况下,我们的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"
}
}
假设现在,我们团队中有一个人开发者决定去完成某个需求,而完成这个需求需要将模块A升级到2.0,而这带来的影响就是模块A依赖于模块B V2.0了,而不是之前的B V1.0。
因此,我们的同时使用npm install来升级到模块A的2.0版本:
npm install mod-a@2 --save
现在我们的目录结构应该是下图:
紧接着,我们的同时完成了这个需求之后,将这个需求推送到测试环境,并且使用npm install去安装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将模块A升级到2.0版本的时候,本质上是最后一个安装模块A V2.0的包。因为我们的开发者之前已经使用npm install来安装package.json中的依赖,这时候因为新需求而升级,就如同是最后一个安装模块A V2.0一般了。
安装模块A V2.0之后,模块A V1.0还是会继续留在顶层依赖中,因为模块E还依赖于模块A V1.0,因此2.0会被安装在模块A的嵌套依赖中。
而对于测试机子的话,项目是在一个新的文件夹中创建,并且之前是没有node_modeuls目录存在的。当npm install命令被运行的时候,它将会安装package.json来安装依赖。
现在,模块A V2.0会首先被安装,并且模块BV 2.0也会被安装在顶层依赖中,这个时候,不在是前面的B V1.0被安装在顶层依赖中。因为模块A V2.0这个时候是首先被安装,而不再是最后被安装。(npm是通过字母表顺序安装依赖的)
紧接着到了模块E被安装的时候,它所依赖的模块B V1.0因为顶层依赖模块B V2.0版本的存在,而被迫得安装在模块E的嵌套依赖之中。
不会的。即使依赖树是不同的,但是通过这个依赖树,两者各自所需要的依赖都能够被安装并且正确地给出。因此,我们所需要的依赖还是能够被提供。
使用npm install去安装同一个package.json文件的时候,总能够输出一致的依赖树。这个是因为安装的时候,都是先安装在package.json中字母靠前的依赖。因此,相同的安装顺序能够让你获得相同的依赖树。
因此当你改变了package.json的时候,你可以通过删除你的node_modules,然后再重新使用npm install来获得一致的依赖树。