npm install 之后发生了什么?

npm install 之后发生了什么?

Well for, one NPM is dark and full of terrors — it’s a wild mix of everything the JavaScript community has to offer. Some great, some bad, but fortunately mostly useful stuff in between the two.

一位程序员分析了1.65M各种版本的npm包之后,写了这篇文章:《What I learned from analysing 1.65M versions of Node.js modules in NPM》。作为一位使用npm多年的老用户,我觉得作者的总结很到位:NPM就像一个黑箱,里面甚至有地雷,它集成了社区成员需要的各种东西。有一些包写得很棒,也有一些很渣,幸好大部分的东西都很有用。文章封面用了那张充满嘲讽意义的图是希望我们能保持警惕,因为你可能不知道安装一个依赖包之后发生了什么事。

为了理解包依赖安装,我阅读了一部分npm源码(主要是install部分),做了一些实验,梳理了package安装流程,整理成下图,以供大家参考。

包依赖安装过程
  1. 如何探索黑箱

除了阅读源代码,其实npm提供了一些方法帮助我们理解 npm install 之后发生了什么。

npm install express --timing=true --loglevel=verbose

上面的命令会把安装过程的log输出到terminal和npm-debug.log里面,例如:

 10 5 silly install runPreinstallTopLevelLifecycles
 11 6 silly preinstall express-lab@1.0.0
 12 7 info lifecycle express-lab@1.0.0~preinstall: express-lab@1.0.0
 13 8 silly install loadCurrentTree
 14 9 silly install readLocalPackageData
 15 10 silly install loadIdealTree
 16 11 silly install cloneCurrentTreeToIdealTree
 17 12 silly install loadShrinkwrap
 18 13 silly install loadAllDepsIntoIdealTree
 19 14 silly currentTree express-lab@1.0.0
 20 14 silly currentTree ├── accepts@1.3.5
 21 14 silly currentTree ├── array-flatten@1.1.1
 22 14 silly currentTree ├── body-parser@1.18.2
 23 14 silly currentTree ├── bytes@3.0.0
 24 14 silly currentTree ├── content-disposition@0.5.2
 25 14 silly currentTree ├── content-type@1.0.4
 26 14 silly currentTree ├── cookie-signature@1.0.6
 27 14 silly currentTree ├── cookie@0.3.1
 28 14 silly currentTree ├── debug@2.6.9
 29 14 silly currentTree ├── depd@1.1.2

通过分析上面log也能基本了解npm安装的过程。

如果你是一个yarn的粉丝,那么可以使用更加优雅的方式:

yarn add express --verbose

相对而言,yarn的输出会更加清晰易读,大爱yarn.

2. 配置管理

verbose 0.315 Checking for configuration file "/mnt/f/Labs/express-lab/.npmrc".
verbose 0.315 Found configuration file "/mnt/f/Labs/express-lab/.npmrc".
verbose 0.316 Checking for configuration file "/home/linson/.npmrc".
verbose 0.316 Found configuration file "/home/linson/.npmrc".
verbose 0.316 Checking for configuration file "/home/linson/.nvm/versions/node/v8.11.2/etc/npmrc".
verbose 0.317 Checking for configuration file "/mnt/f/Labs/express-lab/.npmrc".
verbose 0.317 Found configuration file "/mnt/f/Labs/express-lab/.npmrc".
verbose 0.317 Checking for configuration file "/mnt/f/Labs/.npmrc".
verbose 0.317 Checking for configuration file "/mnt/f/.npmrc".
verbose 0.317 Checking for configuration file "/mnt/.npmrc".

上图是我在yarn的log中截取的一段,其实已经可以清晰地展示了加载.npmrc配置的过程。配置优先级如下:

a. 命令行中的配置,例如npm install --registry=http://****

b. 项目目录中的.npmrc文件

c. 用户主目录(home)的.npmrc文件


2. lockfile

lockfile即是 package-lock.json或者yarn.lock。 从上图中,我们可以看到lockfile发挥了极其重要的作用。一般而言,我们使用lockfile是为了控制安装版本,其实,lockfile还可以提高二次安装的速度。

如果存在lockfile,那么就会直接进入文件完整性检查环节,减少了大量的http请求。


3. 依赖树

无论yarn还是npm都会有依赖树构建过程,而npm更加过分,会在依赖树上处理两次。其实依赖树就是为了让依赖扁平化。如下图:

对依赖进行扁平化有明显的优点:

a. 提高安装速度;

b. 减少node_modules大小;

c. 提高加载速度;

但是依赖分析往往需要用到递归方法,当依赖包非常多时耗时是很大的。根据这个基本原理,我们会发现新增一个依赖包时可能会比较慢,因为需要与所有的现存的依赖包做对比。


总结一下,流程图已经足够清晰地展示了包依赖安装的过程。在整个过程中,lockfile, 包依赖树分析,缓存,资源拉取策略是重要的环节。其中缓存,资源拉取策略会用专门的文章来阐述。

发布于 2018-05-25

文章被以下专栏收录