阅读 69

npm install

Background

假设此时你的repo中没有任何lock文件,当你执行npm install, npm会根据你在package.json中对各种依赖的定义去安装这些依赖。安装完之后,会产生两个结果:

  • node_modules: 所有依赖包

  • npm-lock.json: lock 文件精确描述了 node_modules 目录中所列出的目录的物理树,这个文件相当于是node_module产生的物理树的快照。

package.json里面定义的是版本范围(比如^1.0.0),具体跑npm install的时候安的什么版本,要解析后才能决定,这里面定义的依赖关系树,可以称之为逻辑树(logical tree)。node_modules文件夹下才是npm实际安装的确定版本的东西,这里面的文件夹结构我们可以称之为物理树(physical tree)。安装过程中有一些去重算法,所以你会发现逻辑树结构和物理树结构不完全一样。

package-lock.json可以理解成对结合了逻辑树和物理树的一个快照(snapshot),里面有明确的各依赖版本号,实际安装的结构,也有逻辑树的结构。

去重算法

对复杂的工程,可能需要安装非常多的依赖包,就有可能出现,一个包被多个包依赖,很可能在应用 node_modules 目录中的很多地方被重复安装。随着工程规模越来越大,依赖树越来越复杂,这样的包情况会越来越多,造成大量的冗余。npm3以后,为了解决这个问题,将node_module的包结构变成了变成了扁平的,因此可以避免安装很多重复的包。npm 3 都会在安装时遍历整个依赖树,计算出最合理的文件夹安装方式,使得所有被重复依赖的包都可以去重安装。

npm官方有专门讲过去重算法的问题:

npm中使用指令npm dedupe/npm ddp, 搜索本地包树,并尝试通过将依赖项向上移动到树的更高层来简化整个结构,在树的更高层,多个依赖包可以更有效地共享依赖项。

举个例子:

a+-- b <-- depends on c@1.0.x
|   `-- c@1.0.3`-- d <-- depends on c@~1.0.9
    `-- c@1.0.10

在这种情况,npm run dedup可以将树变成以下的状态:

a
+-- b
+-- d
`-- c@1.0.10

解释:

  • b依赖c

  • d依赖c
    而且bd都依赖的c都是兼容的,因此可以使用同一份package。

根据npm去重原则,会将c上移一个层级方便bd共享。

  • npm将每个依赖项尽可能地向上移动到树的根部,即使这个包不一定被多个包依赖(假设只有一个包依赖)。这样做将产生一个扁平的和去复制的树。

  • 如果这个包A只被一个包依赖,那么尽可能将A向树的高层级移动。

  • 如果在树的目标位置已经存在一个合适的版本,那么它将保持原样,但是其他副本将被删除。

  • 但是每个层级只能有一个类型的包(不同版本也不行)

实例1:

项目中安装了webpack@1.15.0,产生的package-lock如下

    "webpack": {
      "version": "1.15.0",
      "resolved": "https://registry.npmjs.org/webpack/-/webpack-1.15.0.tgz",
      "integrity": "sha1-T/MfU9sDM55VFkqdRo7gMklo/pg=",
      "requires": {
        "acorn": "^3.0.0",
        "async": "^1.3.0",
        "clone": "^1.0.2",
        "enhanced-resolve": "~0.9.0",
        "interpret": "^0.6.4",
        "loader-utils": "^0.2.11",
        "memory-fs": "~0.3.0",
        "mkdirp": "~0.5.0",
        "node-libs-browser": "^0.7.0",
        "optimist": "~0.6.0",
        "supports-color": "^3.1.0",
        "tapable": "~0.1.8",
        "uglify-js": "~2.7.3",
        "watchpack": "^0.2.1",
        "webpack-core": "~0.6.9"
      }
    }

首先可以看到的是,webpack只有require,require中描述的是webpack中package.json的结构,但是没有dependency,说明webpack自己的node_module中啥也没有。

  • require:除最外层的 requires 属性为 true 以外, 其他层的 requires 属性都对应着这个包的 package.json 里记录的自己的依赖项

  • dependency: dependencies 层次结构与文件系统中 node_modules 的文件夹层次结构是完全对照的,这个字段可以理解成是node_module的快照

那么webpack的依赖,比如acorn去哪了?发现他出现在根结构下

{
  "name": "npm",
  "version": "1.0.0",
  "lockfileVersion": 1,
  "requires": true,
  "dependencies": {
    "acorn": {
      "version": "3.3.0",
      "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz",
      "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo="
    },

根据查找,发现整个树上只有webpack依赖这个包,根据

npm将每个依赖项尽可能地向上移动到树的根部,即使这个包不一定被多个包依赖(假设只有一个包依赖)。这样做将产生一个扁平的和去复制的树。

这个包acorn就被移到了树的根部。

实例二:

如果是 A{B,C}, B{C,D@1}, C{D@2} 的依赖关系,得到的安装后结构是:

A+-- B+-- C
   `-- D@2+-- D@1
  • A依赖C,B也依赖C,两个C兼容,因此C被上移到和B一个层级。

  • B依赖D1,C依赖D2,但是两个D不兼容,因此必须安装两个D。

如果这个包A只被一个包依赖,那么尽可能将A向树的高层级移动。

此时D1只被B依赖,因此D1被提高到和B一个层级

  • 如果在树的目标位置已经存在一个合适的版本,那么它将保持原样,但是其他副本将被删除。

  • 但是每个层级只能有一个类型的包(不同版本也不行)

此时D2只能留在原来的层级,因此BC层级已经有一个D

npm install工作流程

常常我都会有一个疑问,npm install的时候到底是根据package.json安装呢?还是根据package-lock安装?

因此我做了一个小实验,安装webpack (npm: 6.7.0/node: v11.13.0):


package.json(before)package-lock.json(before)node_module(before)commandpackage.json(after)package-lock.json(after)node_module(after)
S1^1.8.01.8.0nullinstall^1.8.01.8.01.8.0
S2^1.8.01.8.01.8.0install^1.8.01.8.01.8.0
S3^1.8.01.8.01.8.0up^1.15.01.15.01.15.0
S4^1.8.01.15.01.15.0/nullinstall^1.8.01.15.01.15.0
s5^1.8.03.0.0nullinstall^1.8.01.15.01.15.0
s6^3.0.01.15.0nullinstall^3.0.03.12.03.12.0

summary:

  • npm install首先肯定是希望根据package.json文件安装package。

    • 如果package-lock.json文件中的版本和package.json版本不能匹配(兼容)

      npm会根据package.json允许的package的最高版本安装package并且同时修改package-lock

    • 如果package-lock.json文件中的版本和package.json版本匹配(兼容)。比如s4的情况,一定会根据package-lock安装

  • 最后安装的版本一定和package-lock中的一定一致

根据优先级来看:

npm安装的包一定是遵循package.json中的要求,如果package-lock中版本满足条件,那么完全按照lock中安装,如果不满足要求,安装满足要求的最高版本并且更新lock文件。



作者:张培_
链接:https://www.jianshu.com/p/470e3e82c394

文章分类
后端
文章标签
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐