摘要:我们日常在下载第三方依赖的时候,都会用到一个命令npm install,那么你知道,在运行这个命令的时候都会发生什么吗?

本文分享自华为云社区《运行npm install命令的时候会发生什么?》,作者: gentle_zhou。

npm(node package manager),是随同Node.js一起安装的第三方包管理器;通过npm,我们可以安装、共享、分发代码,管理项目的依赖关系。

我们日常在下载第三方依赖的时候,都会用到一个命令npm install,然后依赖包就会被安装到node_modules目录下;但是我们在运行这个命令的时候都会发生什么呢?带着好奇心,我去调研学习了一番。

大致的流程是:npm install命令输入 > 检查node_modules目录下是否存在指定的依赖 > 如果已经存在则不必重新安装 > 若不存在,继续下面的步骤 > 向 registry(本地电脑的.npmrc文件里有对应的配置地址)查询模块压缩包的网址 > 下载压缩包,存放到根目录里的.npm目录里 > 解压压缩包到当前项目的node_modules目录中。

(上面的图片就显示了项目中依赖如果过多的尴尬:等待时间过长,下载下来依赖过多导致node_modules过大)

下面会介绍一下npm处理依赖的早前和当前的方式 以及 几种不同的install命令下载方式。

早期版本:递归

在npm早期版本里,npm处理依赖的方式很粗暴简单。它会严格按照根目录下package.json文件的结构以及各个子依赖包的package.json文件的结构,递归地把依赖安装到它们各自的node_modules目录里。这样如果是个小项目,只需要几个依赖且这些依赖不会依赖别的依赖,那么这样的树形结构就还算清晰明了(node_modules的结构与package.json里的结构一一对应且层级结构明显)。

但如果我们的项目是个大项目,里面的依赖非常多(导致嵌套层级非常深),且不同的层级可能会引用同一个依赖(导致重复冗余),就不是我们想要的情形了。

当前版本:扁平化

于是,为了解决以上递归管理依赖带来的问题,npm在 3.X版本里做了一次更新,引入了扁平化管理(dedupe)的方式。dedupe是dedeplicated的缩写,即duplicates were remove,把重复的移除。

扁平化管理的思路就是首先遍历package.json文件下dependencies和devDependencies字段里的依赖,作为依赖树的根节点;然后在每个根节点依赖下面都会有其依赖的依赖,作为其子节点;npm会开启多进程从每个根节点开始逐步往下寻找更深层次的节点。而package.json文件下dependencies和devDependencies字段里的依赖会被安装在node_modules根目录下。在遍历这些依赖的时候,如果发现有重复的依赖模块(重复:模块名相同且semantic version兼容;这里的兼容,是指语义化版本都会有一段版本允许范围,如果两个依赖的版本号是在这个范围交际里就说明是兼容;比如依赖X依赖于依赖Y@^1.0.0,而依赖Z依赖于依赖Y@^1.1.0,则Y@^1.1.0就是兼容版本),就直接将其丢弃。

但是如果仅仅这样,其实也有风险。在大项目中,很有可能会碰到依赖A依赖于依赖C-1.0版本,依赖B依赖于依赖C-2.0版本;而在执行npm install命令的时候,会按照package.json里面的依赖顺序依次解析,因此依赖C-1.0和依赖C-2.0的在文件里的放置顺序会导致Node_modules的依赖结构产生变化。而且为了让开发者可以使用最新的依赖包,package.json文件里通常只会锁定大版本(即文件里依赖如果是^1.1.0版本,npm就会去仓库中获取符合1.x.x形式的最新版本),因此某些依赖包小版本更新后,也会造成依赖结构的改变。所以,为了解决npm install命令导致的这种不确定问题,npm 5.x版本里还新增了package-lock.json文件。

package-lock.json文件可以保证每次执行npm install后生成的node_modules目录结构一定是完全相同的。下图就是package-lock.json中其中一个依赖的信息,有name-包名,version-包的版本号,dependencies-和node_modules中包结构一一对应的对象,resolved-包具体的安装来源,integrity-包的hash值,requires-对应子依赖的依赖:

注:并不是所有的子依赖都有dependencies这个属性,只有子依赖的依赖和当前已安装在根目录的Node_modules中的依赖起了冲突之后,才会有这个属性。

置于为何说package-lock.json 文件 和 node_modules 目录结构是一一对应的。还是举刚刚前面提及的那个依赖冲突导致依赖结构产生变化的例子,“依赖A依赖于依赖C-1.0版本,依赖B依赖于依赖C-2.0版本”,此时因为package-lock.json文件的存在,我们会把依赖C-1.0版本安装在依赖A的node_modules目录下(对应依赖A在package.json文件里的dependencies属性),依赖C-2.0版本安装在根目录下。这可以保障每次安装生成的依赖目录结构保持相同。

package-lock.json 文件还有个优点,就是它会缓存每个包的具体版本和下载链接,在后期再去install的时候,就不需要再去远程仓库进行查询操作了,减少了大量网络请求。

几种不同的install命令下载方式

  1. npm install xxx #(XXX是某依赖包)安装依赖模块至项目node_modules目录下,不会修改package.json文件里的内容
  2. npm install -g xxx #安装依赖模块到全局(而不是项目node_modules目录下),不会将该依赖模块写到package.json文件里的dependencies和devDependencies字段里
  3. npm install --save xxx #安装依赖模块到项目node_modules目录下,并将依赖写入到package.json文件里的dependencies字段中;该依赖是开发和生产环境里都需要的
  4. npm install --save-dev xxx #安装依赖模块到项目node_modules目录下,并将依赖写入到package.json文件里的devDependencies字段中

点击关注,第一时间了解华为云新鲜技术~

运行npm install命令的时候会发生什么?的更多相关文章

  1. 当运行npm install 命令的时候带上ignore-scripts,会发生什么?

    摘要:运行npm install 命令的时候带上ignore-scripts, 可以避免一个恶意包里的病毒. 本文分享自华为云社区<运行npm install 命令的时候带上--ignore-s ...

  2. npm install命令详解

    -S,–save 安装包信息将加到dependencies(生产阶段的依赖) npm install --save 或 npm install -S -D, –save-dev 安装包信息将加到dev ...

  3. 关于 npm install 命令

    使用 `npm install` 命令安装模块时 ,有以下几种形式: 安装模块到项目 node_modules 目录下,不会将模块依赖写入 dependencies 或 devDependencies ...

  4. vs2015 打开项目自动运行 npm install

    问题:VS2015(visual studio 2015) 打开项目自动运行  npm install 解决办法: 打开工具-选项-项目与解决方案--外部web工具   去掉npm勾选 还有如果文件g ...

  5. npm install命令对package-lock.json文件自动做了一些额外的更新

    今天我使用 npm 命令给项目安装file-saver,通过git却发现package-lock.json中除了file-saver组件之外的其他组件的记录也被改了 npm为何会自动做这些更改呢,又如 ...

  6. npm install 命令。默认会找到当前路径下的package.json。然后安装其中的依赖

    npm install 命令.默认会找到当前路径下的package.json.然后安装其中的依赖 By default, npm install will install all modules li ...

  7. IDEA -01 -忽略指定文件夹 -防止加载Vue-cli执行"npm install"命令后的项目时卡死

    问题描述 Vue的"npm install" 命令执行后,会生成一个很大的目录层次的"node_modules",文件十分繁多; idea加载这个项目下的文件夹 ...

  8. elasticsearch-head-master下运行npm install报npm WARN elasticsearch-head@0.0.0 license should be a valid SPDX license expression

    2个月没有启动es和es配套服务,今天运行时,发现如下问题: 运行npm install 出现npm WARN elasticsearch-head@0.0.0 license should be a ...

  9. Nodejs中npm install 命令的问题

    在使用nodejs的npm包管理工具中碰到过许多个坑,在网上查了很久才解决,现在加以总结. 两种安装方式(本地安装,全局安装) 1.全局安装(npm install -g moduleName/npm ...

随机推荐

  1. uoj450 【集训队作业2018】复读机(生成函数,单位根反演)

    uoj450 [集训队作业2018]复读机(生成函数,单位根反演) uoj 题解时间 首先直接搞出单个复读机的生成函数 $ \sum\limits_{ i = 0 }^{ k } [ d | i ] ...

  2. CF1486X Codeforces Round #703

    C2 Guessing the Greatest (二分+构造) 题目大意:交互题,每次可以询问一个子区间次大值的位置,最多询问20次,问全局最大值的位置.n=1e5 40次的情况大力二分,20次需要 ...

  3. 使用Web Deploy自动打包发布

    在内部测试阶段,经常改一点小东西需要更新给测试继续测试.然后就需要频繁的找到对应更改的视图/JS文件,或者是编译的dll文件,再打开测试服务器找到对应站点替换进去,整套流程下来就非常的繁琐费时. 使用 ...

  4. Vscode的使用小技巧

    命令行启动code 如果你的系统是Linux系统(我使用的是Ubuntu 16.04)这样就可以直接使用 code + filename来编辑文件(就像vi + filename) 如果你的系统是Ma ...

  5. Configuration problem: 'bean' or 'parent' is required for <ref> element

    我出现此错误的原因是web.xml中没有指定spring的启动配置文件applicationContext.xml的加载位置.applicationContext.xml原来再webRoot/webI ...

  6. C语言break,return

    C语言break,continue,return的相似与区别 相同点: 都改变了程序的执行流程 区别是:break    用于循环和switch分支,跳出它所在分支或循环体到它所在的模块的      ...

  7. Numpy实现SVD矩阵分解

    1. 引入包 2. 实现矩阵分解 3. 从分量还原矩阵

  8. task0002(四)- 练习:数据处理、轮播及交互

    转载自我的个人博客 欢迎大家批评指正 包括5部分: 小练习1-处理用户输入 小练习2-日期对象的使用 小练习3:轮播图 小练习4:输入提示框 小练习5:界面拖拽交互 源码地址task0002 在线De ...

  9. ES6-11学习笔记--正则表达式的扩展

    y修饰符 u修饰符   复习以前的修饰符: i(忽略大小写) m(多行匹配) g(全局匹配)   y修饰符:粘连修饰符 const str = 'aaa_aa_a' const reg1 = /a+/ ...

  10. for 循环详解

    学习目标: 掌握 for 循环的使用 学习内容: 1.for语法 for(初始化语句; boolean表达式; 循环后操作语句) { 循环体; } 流程图如下: 特点: 初始化语句:只在循环开始时执行 ...