当执行 npm install 时,npm会查看 package-lock.json
文件,根据其中列出的 version
、resolved
、integrity
字段来确定需要安装的确切包版本。如果本地缓存中已经存在与 resolved
、integrity
匹配的包,npm就会从缓存中提取该包,如果本地缓存中没有找到匹配的包,或者 package-lock.json
文件中的 integrity
校验和缓存中的包不匹配,npm就会从 resolved
指定的 URL 下载包,并将其添加到本地缓存中供未来使用。
.npmrc
.npmrc
.npmrc
npm i
在安装依赖时使用的算法称为“npm依赖解析算法”,以下是基本步骤:
读取项目的 package.json
文件,获取项目的依赖列表。
对于每一个依赖,npm会查找是否已经安装了满足版本要求的包。
在下载包的过程中,npm会检查包的 package.json
文件,看看包是否还依赖了其他的包。如果有,npm会重复上面的步骤,直到所有的依赖都被解决。
在解决依赖的过程中,npm会尽可能地重用已经安装的包,以减少需要下载包的数量。这是通过一个叫做“最大化版本满足”的策略来实现的。
最后,npm会生成一个 node_modules
目录,这个目录中包含了所有下载的包,以及它们的依赖。这个目录的结构是树形的,每个包都是树的节点,依赖的包是子节点。
首先安装的依赖都会存放在根目录的
node_modules
,默认采用扁平化的方式安装,并且排序规则.bin第一个然后@系列,再然后按照首字母排序abcd等,并且使用的算法是广度优先遍历,在遍历依赖树时,npm会首先处理项目根目录下的依赖,然后逐层处理每个依赖包的依赖,直到所有依赖都被处理完毕。在处理每个依赖时,npm会检查该依赖的版本号是否符合依赖树中其他依赖的版本要求,如果不符合,则会尝试安装适合的版本
扁平化只是理想状态下:
安装某个二级模块时,若发现第一层级有相同名称,相同版本的模块,便直接复用那个模块
因为A模块下的C模块被安装到了第一级,这使得B模块能够复用处在同一级下;且名称,版本,均相同的C模块
非理想状态下:
因为B和A所要求的依赖模块不同,(B下要求是v2.0的C,A下要求是v1.0的C )所以B不能像2中那样复用A下的C v1.0模块 所以如果这种情况还是会出现模块冗余的情况,他就会给B继续搞一层node_modules,就是非扁平化了。
“最大化版本满足” 是 npm 在解析和安装依赖时采用的一种策略。
这种策略的目标是尽可能的重用已经安装的包,以减少需要下载的包的数量。
具体来说,它的工作原理如下:
当一个项目需要一个特定的包时,npm会查看已经安装的包,看是否有一个版本可以满足项目的需求。如果有多个版本都可以满足需求,npm会选择一个最新的版本。这样如果后来又有其他项目需要这个包,就有更大的可能性可以重用这个已经安装的包。
例如:
假设项目A需要包B的1.0.0版本,项目C需要包B的1.0.1版本。
如果先安装项目A,然后安装项目C,npm会先安装包B的1.0.0版本,然后再安装1.0.1版本。但是如果使用了最大化版本满足的策略,npm会之间安装1.0.1版本,这样就可以满足两个项目的需求,而且只需要下载一次包。
这个策略可以提高安装的速度,同时也可以减少磁盘空间的使用。但是它也可能会导致一些问题,例如版本冲突。因此,npm在实际应用中会结合其他策略,如语义版本控制,来确保依赖的正确性。
语义版本控制,也称为SemVer,是一种版本命名规范。
它规定了版本号的格式以及版本号的递增规则,使得版本号能够传达关于底层代码改动的一些信息。
语义版本号由三部分组成:主版本号、次版本号和修订号,格式为:主版本号.次版本号.修订号
,例如:1.2.3
。
使用语义版本控制,可以清晰地了解每个版本之间的差异,也方便依赖管理。在使用npm等包管理工具时,可以通过语义版本控制来精确地指定依赖包的版本,也可以使用一些通配符来灵活地匹配多个版本。
通配符 | 含义 |
---|---|
~ | 匹配当前次版本号下的最大版本,比如 ~1.2.3 会匹配所有 1.2.x 的版本,但是最小版本是 1.2.3 。如果有 1.2.4 和 1.2.5 这样的版本,它会优先匹配 1.2.5 这个更高的版本。但是,它不会匹配 1.3.0 这样次版本号变化的版本。 |
^ | 匹配当前主版本号下的最大版本,比如 ^1.2.3 会匹配所有 1.x.x 版本,但是最小版本是 1.2.3 。如果有 1.4.2 和 1.3.9 这样的版本,它会优先匹配 1.4.2 这个更高的版本。但是,它不会匹配 2.0.0 这样主版本号变化的版本。 |
* | 匹配任何版本 |
>= | 匹配大于等于指定版本的任何版本 |
<= | 匹配小于等于指定版本的任何版本 |
> | 匹配大于指定版本的任何版本 |
< | 匹配小于指定版本的任何版本 |
"" | 匹配任何版本 |
在 Node.js 的 node_modules 目录下,包的排序规则并不是有 Node.js 决定的,而是由你的文件系统决定的。不同的文件系统可能有不同的排序规则。
在大多数情况下,文件系统通常会按照字母顺序进行排序。在ASCII编码中,特殊字符(如 .
和 @
)的编码值都比字母和数字小,因此它们通常排在前面。
node_modules
,默认采用扁平化的方式安装,并且排序按照先 .
再 @
,再然后按照首字母排序,并且使用的算法是广度优先遍历,在遍历依赖树时,npm会首先处理项目根目录下的依赖,然后逐层处理每个依赖包的依赖,直到所有依赖都被处理完毕。在处理依每个赖时,npm会检查该依赖的版本号是否符合当前依赖树中其他依赖的版本要求,如果不符合,则会尝试安装适合的版本。