npm 内部机制与核心原理
npm 最重要的任务是安装和维护开源库。它会优先安装依赖包到当前的项目目录,使得不同的项目的依赖各成体系,同时还能减轻包作者的 API 兼容性压力,但是这样做的缺陷也很明显:如果项目 A 和项目 B 都依赖相同的公共库 C,那么公共库 C 将会在项目 A 和项目 B 中各被安装一次。这就说明,同一个依赖包可能在电脑上多次安装。
npm 的安装机制
当我们执行npm install
命令以后,首先检查 config,获取 npm 配置。这里的优先级为:项目级的.npmrc 文件 > 用户级的.npmrc 文件 > 全局的.npmrc 文件 > npm 内置的.npmrc 文件。
然后检查项目中有无 package-lock.json 文件(简称 lock 文件)
如果有 package-lock.json 文件,那么就检查 package-lock.json 和 package.json 文件中声明的版本是否一致。
一致,直接使用 package-lock.json 中的信息,从缓存或者网络资源中加载资源。
不一致,那么根据 npm 版本进行处理(5.4.2 以上会按照 package.json 安装,并更新 package-lock.json)
如果没有 package-lock.json 文件,那么根据 package.json 文件递归构建依赖树,然后按照构建好的依赖树下载完整的依赖资源,在下载时会检查是否有相关缓存。
有缓存,将内容解压到 node_modules 中。
没有,先从 npm 远程仓库中获取包资源,检查包的完整性,并将其添加到缓存,同时解压到 node_modules 中。
最后生成 package-lock.json 文件。
npm 缓存机制
npm install 执行时,会通过 pacote 将相应的包资源解压到对应的 node_modules 下面。npm 下载依赖时,会先将依赖下载到缓存中,再将其解压到项目的 node_modules 下。pacote 依赖 npm-registry-fetch 来下载包资源,npm-registry-fetch 可以通过设置 cache 属性在给定的路径下根据 IETF RFC 7234 生成缓存数据。
接着每次安装资源时,根据 package-lock.json 中存储的 integrity、version、name 信息生成一个唯一的 key,这个 key 能够对应到 index-v5 下的缓存记录。如果发现有缓存资源就会找到 tar 包的 hash 值,根据 hash 值找到缓存的 tar 包,并再次通过 pacote 将对应的二进制文件解压到相应的项目 node_modules 下,省去了从网络下载资源的时间。
npm 不完全指南
自定义 npm init
npm 支持自定义 npm init,快速创建一个符合自己需求的自定义项目。npm init 命令本身并不复杂,它的功能其实就是调用 Shell 脚本输出一个初始化的 package.json 文件。相应的我们需要自定义 npm init 命令。就是写一个 node.js 脚本,它的 module.exports 就是 package.json 配置内容。
为了实现更加灵活的自定义功能,我们可以使用 prompt()方法来获取用户输入的内容及动态产出的内容。
1 | const desc = prompt('请输入项目描述', '项目描述...') |
假设上面脚本的名字为.npm-init.js 执行以下命令来确保 npm init 所对应的脚本指向正确的文件。
1 | npm config set init-module ~\.npm-init.js |
我们也可以通过配置 npm init 默认字段来自定义 npm init 的内容。如下:
1 | npm config set init.author.name "Guozhaoxi" |
利用 npm link 高效进行本地调试以验证包的可用性
如何高效的在本地调试以验证包的可用性呢?这个时候我们可以使用 npm link 命令。简单来说,它可以将模块链接到对应的业务项目中运行。
npm link的本质就是创建软链接。它主要做了两件事:
- 为目标 npm 模块创建软链接将其连接到/usr/local/lib/node_modules/全局模块安装路径下
- 为目标 npm 模块的可执行 bin 文件创建软链接,将其链接到全局 node 命令安装路径/usr/local/bin 下。
npx
npx 为什么操作起来更便捷?
这是因为它可以直接运行 node_modules/.bin 文件夹下的文件。在运行命令时,npx 可以自动去 node_modules/.bin 路径和环境变量$PATH 里面检查命令是否存在,而不需要在 package.json 中定义相关的 script。
npx 另一个更实用的特点是,它在执行模块时会优先安装依赖,但是在安装成功后便删除依赖。避免了全局安装带来的问题。