npm安装流程

799 阅读5分钟

安装流程

  1. npm install执行之后,首先检查并获取npm配置,优先级:项目级.npmrc文件>用户级.npmrc文件>全局的.npmrc文件>npm内置.npmrc文件

  2. 然后检查项目是否有package-lock.json文件

    1. 如果有,则检查package-lock.json和和package.json中声明的依赖是否一致

      •     一致,直接使用package-lock.json中的信息,从缓存或网络资源中加载依赖
        
      •     不一致,按照npm版本进行处理,不同npm版本处理会不同
        
    2. 如果没有,则根据package.json递归构建依赖树

  3. 然后按照构建好的依赖树下载完整的依赖资源,在下载时会检查是否存在相关资源缓存

    1. 存在,则将缓存内容解压到node_modules中
    2. 不存在,从npm远程仓库下载包,检查包的完整性,并添加到缓存,同时解压到node_modules
  4. 最后生成package-lock.json

package-lock.json文件作用

package-lock.json是在npm install时生成的文件,用以记录当前状态下实际安装的各个npm package的具体来源和版本号,在npm 5.x版本新增了package-lock.json文件。package-lock.json文件的作用是锁定安装时包的版本号,并且需要上传到git,以保证其他人在npm install时大家的依赖能保证一致。

不同npm版本生成依赖树区别

npm 3.x版本之前是嵌套结构

由于npm中的依赖会依赖其他依赖,npm 3.x版本之前的策略是完全按照目录结构安装整个依赖树。好处是依赖树比较清晰易懂,坏处就是导致很多重复的以来被重复安装

图中依赖A被其他依赖依赖,同一份依赖理论上只需要安装一次,但是在npm 3.x版本之前的策略中依赖被依赖一次就要安装一次,除了大量重复的依赖被安装浪费磁盘空间之外,还有其他两个问题:

  1. 依赖层级过深,会存在文件路径过长问题。在windows下,很多程序无法处理超过260个字符
  2. 无法共享实例,有一些程序要求在运行时只能有一个实例。由于安装了多个依赖,无法共享同一份代码

npm 3.x版本之后是平铺结构

为了解决依赖重复安装的问题,npm 3.x版本和yarn一样,采用扁平依赖树来管理依赖包,重复的依赖会提升到最上层

为什么要这样做呢?要从node的require机制说起,当我们通过require引入外部模块时,会判断当前node_modules文件夹是否有此模块,有就返回,没有就递归往上层的node_modules目录查找,如果找到就返回,如果到根目录都没找到就报错。

扁平依赖树解决了嵌套结构的问题,但是要生成扁平依赖的结构,就需要先遍历所有项目依赖关系来构建完整的依赖树,以此来决定生成的目录结构,这也造成了安装慢的问题。同时,因为在依赖树中,可能存在同一个依赖不同版本的情况,提升只能提升一个,所以后面再遇到相同包的不同版本,依然还是会嵌套。

npm 5.0.x

根据package-lock.json下载

npm 5.1.0 ~ 5.4.2

当package.json声明的依赖版本规范有符合的更新版本时,忽略package-lock.json,按照package.json安装,并更新package-lock.json

npm 5.4.2以上

当package.json声明的依赖版本规范与package-lock.json安装版本兼容,则根据package-lock.json安装;如果package.json声明的依赖版本规范与package-lock.json安装版本不兼容,按照package.json安装,并更新package-lock.json

npm缓存机制

npm config get cache查看本地缓存,缓存文件放在.npm根目录下的_cacache中,可以通过npm cache clear --force清除缓存

  • 在安装资源的时候,npm会根据package-lock.json中的integrity、version、name信息生成一个唯一的key
  • 然后用key经过加密算法生成一个hash,然后在系统中安装npm文件夹中的index-v5中找到对应的缓存文件,该缓存文件记录着该包的信息
  • 根据该文件中的信息我们在content-v2中找到对应的压缩包,这样就找到对应的缓存资源了
  • 最后将该压缩包解压到node_modules中,节省网络开销和安装时间
  • 这里的缓存策略是从npm 5.x版本开始的,之前每个缓存的模块直接以模块名的形式直接存储

npm如何检查资源的完整性

在下载依赖包之前可以获取到npm对该依赖包计算的hash值(shasum字段),只需要在下载完成后在本地用相同算法再次计算得出一个hash,对比两个值,如果相同就代表下载的依赖是完整的

版本号中^和~含义

所有的版本号都有3个数字:x.y.z,分别代表主版本、次版本、补丁版本

  • ^:当安装依赖时获取到有新版本时,保持主版本号不变的情况下,保持次版本号、修订版本号为最新版本
  • ~:当安装依赖时获取到有新版本时,保持主版本号、次版本号不变的情况下,保持修订号的最新版本

注意📢,当主版本号为0的情况,会被认为是一个不稳定版本,情况与上面不同:

  • 主版本号为 0:^0.y.z 表现和 ~0.y.z 相同,只保持修订号为最新版本。
  • 主版本号和次版本号都为 0::^0.0.z、~0.0.z 都被当作固定版本,安装依赖时均不会发生变化。

npm允许这样安装,是因为相信依赖的作者是完全遵守语义化版本控制规范的,也就是主版本的最新版本的API是兼容老版本的API的

npm安装时-S和-D区别

npm install安装默认会放在生产依赖中

-S

即--save,包名会被注册在package.json的dependencies(简称dep)里,是在生产环境要用到的

例如:

vue、react、axios、element ui,程序运行要使用的,放在生产依赖中

-D

即--save -dev,包名会被注册在package.json的devDependencies(简称dev)里,是在开发环境要用到的

例如:

babel、loader、css预处理器、eslint、webpack,一般都是辅助工具,程序实际运行的时候并不需要,放在开发依赖中