深挖 NPM 机制

运用NPM装置的时刻会经常出现包争执(比方多个主模块的子模块版本不一致等),致使在开辟历程中会碰到种种或大或小的题目。一切在这会引见以下内容:

  1. NPM 重要装置体式格局
  2. NPM 包信息查询
  3. NPM 装置机制(重要)

装置&查询敕令

NPM 种种装置体式格局

  • npm install packageName[@next | @versionNumber]

    • 在 node_modules 中没有指定模块时装置,(不搜检~/.npm目次)
  • npm install packageName --f | -- force

    • 一个模块不论是不是装置过,npm都要 强迫重新装置
  • npm update packageName

    • 假如长途版本较新、或许当地版本不存在时装置

NPM 查询效劳

  • NPM经由过程registry的查询效劳,从而晓得每一个模块的最新版本。
  • 能够经由过程 npm view packageName [version] 查询对映模块的信息

NPM 装置机制

输入 npm install 敕令并敲下回车后,会阅历以下几个阶段(以 npm 5.5.1 为例):

1. 实行工程自身 preinstall

当前 npm 工程假如定义了 preinstall 钩子此时会被实行。

2. 肯定首层依靠模块

起首须要做的是肯定工程中的首层依靠,也就是 dependenciesdevDependencies 属性中直接指定的模块(假定此时没有增加 npm install 参数)。

工程自身是整棵依靠树的根节点,每一个首层依靠模块都是根节点下面的一棵子树,npm 会开启多历程从每一个首层依靠模块最先逐渐寻觅更深层级的节点。

假如查询node_modules目次当中已存在指定模块,那末不再重新装置

3. 猎取模块

猎取模块是一个递归的历程,分为以下几步:

  • 猎取模块信息

    • 在下载一个模块之前,起首要肯定其版本,这是由于 package.json 中往往是 semantic version(semver,语义化版本)
    • 此时假如版本形貌文件(npm-shrinkwrap.json 或 package-lock.json)中有该模块信息直接拿即可
    • 假如没有则从堆栈猎取(向registry查询)。如 packaeg.json 中某个包的版本是 ^1.1.0,npm 就会去堆栈中猎取相符 1.x.x 情势的最新版本。
  • 猎取模块实体。

    • 上一步会猎取到模块的压缩包地点(resolved 字段),npm 会用此地点搜检当地缓存,缓存中有就直接拿,假如没有则从堆栈下载。
  • 查找该模块依靠

    • 假如有依靠则回到第1步,假如没有则住手。

4. 模块扁平化(dedupe)

一步猎取到的是一棵完全的依靠树,个中能够包括大批反复模块。比方 A 模块依靠于 loadsh,B 模块一样依靠于 lodash。在 npm3 之前会严厉根据依靠树的构造举行装置,因此会形成模块冗余。

npm3 版本 最先默许加入了一个 dedupe 的历程。它会遍历一切节点,逐一将模块放在根节点下面,也就是 node-modules 的第一层。当发现有反复模块时,则将其抛弃。

这里须要对反复模块举行一个定义,它指的是模块名雷同且 semver 兼容。每一个 semver 都对应一段版本许可局限,假如两个模块的版本许可局限存在交集,那末就能够获得一个兼容版本,而没必要版本号完全一致,这能够使更多冗余模块在 dedupe 历程中被去掉。

比方

  • node-modules 下 foo 模块依靠 lodash@^1.0.0,bar 模块依靠 lodash@^1.1.0,则 ^1.1.0 为兼容版本。
  • 而当 foo 依靠 lodash@^2.0.0,bar 依靠 lodash@^1.1.0,则根据 semver 的划定规矩,两者不存在兼容版本。会将一个版本放在 node_modules 中,另一个仍保存在依靠树里。

举个例子,假定一个依靠树原本是如许:

node_modules
— foo
—- lodash@version1

— bar
—- lodash@version2

假定 version1 和 version2 是兼容版本,则经由 dedupe 会成为下面的情势:

node_modules
— foo

— bar

— lodash(保存的版本为兼容版本)

假定 version1 和 version2 为非兼容版本,则背面的版本保存在依靠树中:

node_modules
— foo
— lodash@version1

— bar
—- lodash@version2

5. 装置模块

这一步将会更新工程中的 node_modules,并实行模块中的性命周期函数(根据 preinstall、install、postinstall 的递次)。

6. 实行工程自身性命周期

当前 npm 工程假如定义了钩子此时会被实行(根据 install、postinstall、prepublish、prepare 的递次)。

末了一步是天生或更新版本形貌文件,npm install 历程完成。

End

文章分享同步于:
https://github.com/zhongmeizh…

参考

    原文作者:zhongmeizhi
    原文地址: https://segmentfault.com/a/1190000018684918
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞