Vue/前端开发

npm install

2019-03-31  本文已影响0人  张培_

Background

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

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

解释:

根据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只被一个包依赖,那么尽可能将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) command package.json(after) package-lock.json(after) node_module(after)
S1 ^1.8.0 1.8.0 null install ^1.8.0 1.8.0 1.8.0
S2 ^1.8.0 1.8.0 1.8.0 install ^1.8.0 1.8.0 1.8.0
S3 ^1.8.0 1.8.0 1.8.0 up ^1.15.0 1.15.0 1.15.0
S4 ^1.8.0 1.15.0 1.15.0/null install ^1.8.0 1.15.0 1.15.0
s5 ^1.8.0 3.0.0 null install ^1.8.0 1.15.0 1.15.0
s6 ^3.0.0 1.15.0 null install ^3.0.0 3.12.0 3.12.0

summary:

根据优先级来看:

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

上一篇下一篇

猜你喜欢

热点阅读