来源:Node.js

扁平化

扁平化是npm项目理想状态下的依赖结构

理想状态

安装某个次级模块时,若发现上层具有相同名称、相同版本的模块,便可直接复用

在上图中,因为A模块依赖的C 1.0模块被安装到了第一级,因此B模块能够复用第一级的C 1.0模块

非理想状态

A和B所要求的依赖模块不同,A依赖C 1.0、B依赖C 2.0,所以B不能复用A下的C 1.0模块

所以类似这种情况还是会出现模块冗余,B也会出现一层node_modules,变得非扁平化了

npm install

npm install的全流程:

构建依赖树和扁平化都会进行,并非逻辑分支

.npmrc

.npmrc是npm配置文件,分有四个层级

  • 项目级.npmrc:位于项目根目录
  • 用户级.npmrc:位于C盘用户文件夹
  • 全局.npmrc:位于C盘AppData
  • npm内置.npmrc:位于Nodejs安装路径

.npmrc可以设置如下配置项:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
registry=http://registry.npmjs.org/
# 设置npm的registry,即npm的包下载源

proxy=http://proxy.example.com:8080/
# 设置npm的代理服务器,用于访问网络

https-proxy=http://proxy.example.com:8080/
# 设置npm的https代理服务器,用于访问网络

strict-ssl=true
# 是否在SSL证书验证错误时退出

cafile=/path/to/cafile.pem
# 定义自定义CA证书文件的路径

user-agent=npm/{npm-version} node/{node-version} {platform}
# 自定义请求头中的User-Agent

save=true
# 安装包时是否自动保存到package.json的dependencies中

save-dev=true
# 安装包时是否自动保存到package.json的devDependencies中

save-exact=true
# 安装包时是否精确保存版本号

engine-strict=true
# 是否在安装时检查依赖的node和npm版本是否符合要求

scripts-prepend-node-path=true
# 是否在运行脚本时自动将node的路径添加到PATH环境变量中

node_modules

安装的依赖都会存放在项目根目录的node_modules,默认采用扁平化的方式安装

node_modules内的文件按首字符顺序排序,.开头的文件夹在先、@开头的文件夹其次、最后是其他文件夹

执行npm install时,会使用广度优先遍历算法(先处理完所有同级,再开始处理下级)遍历依赖树时:

  1. 首先处理项目根目录下的依赖
  2. 然后逐层处理每个依赖包的依赖
  3. 层层递进,直到所有依赖都被处理完毕

在处理每个依赖时,npm会检查该依赖的版本号是否符合依赖树中其他依赖的版本要求,如果不符合,则会尝试安装适合的版本

package-lock.json

文档

package-lock.json可以锁定版本号,记录依赖树详细信息,还会为包作缓存

packages项会记录一些做过扁平化处理的依赖,这些记录以包名name为属性名,一个对象为属性值,该对象含有一些属性:

  • version:当前包的版本号
  • resolved:当前包的下载地址
  • integrity:验证包的完整性
  • dev:是否为开发依赖包
  • bin:当前包中可执行文件的路径和名称
  • engines:当前包所依赖的Nodejs版本范围

它会通过name + version + integrity格式生成一个唯一的key,这个key能在npm-cache/_cacache/index-v5文件夹寻找对应的缓存记录

若有缓存记录,就会据此在npm-cache/_cacache/context-v2找到tar包的hash值,并将对应的二进制文件解压到node_modules