在执行npm install 的时候发生了什么?
- 解析
package.json
文件 :
- npm首先读取项目根目录下的
package.json
文件。- 解析该文件中的
dependencies
和devDependencies
对象,获取项目所需的所有依赖包及其版本号。- 查询并解析依赖 :
- 对于每个依赖,npm会查询npm注册中心,找到与
package.json
中指定的版本号相匹配的依赖版本。- 如果依赖还包含自己的依赖(子依赖),npm也会递归地进行查询和解析(需要注意的是,这里采用的是广度优先算法)。
- 检查本地缓存 :
- npm会检查本地缓存中是否已经存在所需依赖的副本。
- 如果本地缓存中已有所需版本的依赖,则直接从缓存中获取,而不会从npm注册中心下载。
- 下载依赖 :
- 对于本地缓存中不存在的依赖,npm会从npm注册中心下载依赖包。
- 下载的依赖包包含了依赖的代码和它自己的
package.json
文件。- 安装依赖 :
- 下载后的依赖包被解压到
node_modules
目录中。- 如果依赖包含子依赖,npm也会递归地安装这些子依赖。
- 执行生命周期脚本 :
- 在安装过程中,npm会检查依赖的
package.json
文件中是否定义了生命周期脚本(如preinstall
、install
、postinstall
等)。- 如果有定义,npm会在相应的时机执行这些脚本。
- 生成或更新
package-lock.json
或npm-shrinkwrap.json
文件 :
- npm会生成或更新
package-lock.json
文件,以确保未来安装时能够得到相同版本的依赖。- 这有助于项目在不同环境和不同开发者之间保持依赖的一致性。
- 整理
node_modules
目录结构 :
- 为了避免冗余和冲突,npm可能会对
node_modules
目录进行整理,优化依赖的存储结构。- 从npm v3开始,尽可能将依赖扁平化,以减少目录深度和重复的依赖副本。
- node_modules文件夹内部文件在进行排序的时候,以.开头的放在最前面,例如启动命令的.bin文件夹,接着就是@开头的文件夹,最后则是普通的文件夹
真的能做到扁平化吗?
- 扁平化只是理想的状态
- 如上图,Vue和React都用到同一个babel,且版本和名称都一样。所以babel会提到这个相同等级(一级模块)的一个依赖。
- 所以
Vue
和React
就能够复用同一个Babel
模块,而不至于两个框架都要重新装一遍Babel
,这样对于节省我们的电脑存储空间有好处。毕竟npm包是很大的,一不小心就占掉电脑好几G了 - 上面就是一种理想状态下的扁平化,扁平扁平就是同一层级下的意思(但需要名称、版本,均相同)
非理想状态
- 当要复用的那个模块,需要的版本不同时。就像下面就不知道要复用C1.0还是C2.0
- 像这种不理想情况下,就会单独安装,出现模块冗余的情况,给B继续搞一层node_modules,就无法节省空间了(非扁平化)
npm install 后续流程
该图的流程如下:
- 执行
npm install
:- 用户在终端或命令行界面中输入
npm install
命令并执行。
- 用户在终端或命令行界面中输入
- 读取配置文件 :
npm
查看是否有配置文件(如.npmrc
),可能存在于项目目录(局部配置,先找这个)或用户主目录(全局配置,后找这个)中,如果都没找到就会去找npm内置的。npm
根据这些配置文件来决定如何进行安装,例如代理服务器、镜像源等设置。
- 解析
package.json
和package-lock.json
文件 :npm
读取并解析项目中的package.json
和package-lock.json
文件来确定要安装的依赖包及版本。
- 检查 node_modules 目录和 package-lock.json :
npm
检查node_modules
目录和package-lock.json
文件,确定是否已经存在满足版本要求的依赖包。- 如果
package-lock.json
和package.json
版本不一致,并且npm
版本是5.4(高版本)及以上,那么将会优先按照package.json
中记录的版本来安装,并且更新lock文件。 package-lock.json
和package.json
版本一致的话,就会遵循lock文件了
- 安装依赖 :
- 根据解析出来的依赖信息,
npm
开始安装依赖到node_modules
目录中。 - 如果在
node_modules
中检查缓存,已经存在符合版本要求的包,则不会重复安装,直接就解压了,没有就走另一条下载包资源=>检查完整性=>添加到缓存=>更新package.lock.json文件=>解压到node_modules
的道路。
- 根据解析出来的依赖信息,
- 生命周期脚本执行 :
- 在依赖安装的不同阶段,会执行相关的生命周期脚本,如
preinstall
、install
、postinstall
等。
- 在依赖安装的不同阶段,会执行相关的生命周期脚本,如
- 生成或更新 package-lock.json 文件 :
- 在安装过程结束后,
npm
会生成或更新package-lock.json
文件。 - 这保证了以后在其他环境中运行
npm install
能够安装到相同版本的包。
- 在安装过程结束后,
npmrc配置信息
npmrc
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环境变量中
package-lock.json 的作用
- 这个东西不仅可以锁定版本记录依赖树详细信息,还有如下作用
- version 该参数指定了当前包的版本号
- resolved 该参数指定了当前包的下载地址
- integrity 用于验证包的完整性,是一串哈希值
- dev 该参数指定了当前包是一个开发依赖包(参数需要是true)
- bin 该参数指定了当前包中可执行文件的路径和名称,也就是说有bin就表示这里有可执行文件
- engines 该参数指定了当前包所依赖的Node.js版本范围
package-lock.json 帮我们做了缓存,他会通过
name + version + integrity
信息生成一个唯一的key,这个key能找到对应的index-v5 下的缓存记录 也就是npm cache 文件夹下的
- 通过
npm config list
在终端查找缓存文件在哪
- 通过这个路径进行查找文件
- index-v5是一个索引目录,记录content-v2的一个索引或者说是位置,也就是
name + version + integrity
的一个哈希值。如果lock锁文件内的这三者和index-v5能够对上,就会去content-v2找到你缓存的那个文件。- 其实就是把项目中
name + version + integrity
组成的哈希值的看成一个钥匙就够了,而content-v2
则是一个宝箱,index-v5
则是一个钥匙孔。他们之前的关系就非常清晰了
- 其实就是把项目中
name + version + integrity
哈希值 :这相当于是一个钥匙。在npm中,每个包都有一个唯一的名称(name
)、版本号(version
),以及一个完整性校验值(integrity
),这个校验值通常是一个SHA值,用于确保包的内容没有被篡改。将这三者组合起来,就形成了一个能唯一标识和验证一个包的"钥匙"。- content-v2:这个目录可以被看作是一个宝箱。它存储了你电脑上缓存的npm包的实际内容。每个缓存的内容都通过一种散列算法(如SHA-512)生成了一个独特的哈希值。
- index-v5 :这个目录就像一个钥匙孔,或者说是一本索引册。它记录了缓存内容的索引信息,这些信息将包的名字、版本和完整性校验值映射到它们在
content-v2
宝箱中的具体位置。当运行
npm install
时,如果package-lock.json
中的包信息(即钥匙)与index-v5
的索引信息(即钥匙孔)匹配,npm就知道它可以直接使用content-v2
中对应的缓存内容(即宝箱里的宝藏),而无需重新下载相同的包。这种机制加快了安装过程,同时确保了安装的一致性和包的完整性。