前端基础建设与架构-01(JS包管理工具与原理分析)
JS包管理工具与原理分析
npm
npm介绍
-
核心目标:给你和你的团队、你的公司带来更好的开源库和依赖(Bring the best of open source you,your term and your company)。
-
npm基本使用
# 检查npm是否已经安装以及安装的版本
npm -v
# 初始化一个package.json文件
npm init
# 安装依赖
# 不带包名:npm将会根据当前目录中的package.json文件安装所有依赖项
# 无参数:默认将包安装到项目的node_modules目录下,不会添加到package.json文件
# --global或-g:将包安装到全局环境,通常用于工具类包
# --save或-S:将包添加到package.json的dependencies字段,生产环境的依赖
# --save-dev或-D:将包添加到package.json的devDependencies字段,开发环境的依赖
# --production:仅安装生产环境所需的依赖项,跳过开发依赖项
# --legacy-peer-deps:允许使用旧版npm的peerDependencies行为
# --no-save:安装包但不将其添加到package.json文件中的依赖项中
# --save-optional或-O:将包安装到可选环境,自动添加到optionalDependencies
# --workspace或-w:支持多工作区安装,适用于大型项目,可以独立管理不同部分的依赖
# --workspaces或-ws:禁用工作区,适用于不需要多工作区管理的项目
npm install <package-name>
npm i
npm add
# 更新依赖
npm update <package-name>
# 卸载依赖
npm uninstall <package-name>
# 运行脚本
npm run <script-name>
npm安装机制
-
检查并获取npm配置,这里的优先级为:项目级的.npmrc文件 > 用户级的.npmrc 文件 > 全局级的.npmrc 文件 > npm内置的.npmrc 文件。
- .npmrc是一个配置文件,用于存储与npm相关的配置信息。在.npmrc文件中,你可以设置各种配置选项,例如:设置代理服务器、设置镜像源、登录到私有仓库、设置缓存位置。
-
package-lock.json与package.json声明的依赖版本不一致时:
- Npm v5.0.0前:npm锁定版本的方式是使用npm-shrinkwrap.json;
- Npm v5.0.x:根据package-lock.json下载;
- Npm v5.1.0 - v5.4.2:当package.json声明的依赖版本规范有符合更新版本时,忽略package-lock.json,按照package.json安装,并更新package-lock.json;
- v5.4.2以上:当package.json声明的依赖版本规范与package-lock.json安装的版本兼容,则根据package-lock.json安装;如果不兼容,按照package.json安装,并更新package-lock.json。
-
构建依赖树时,当前依赖项目不管其是直接依赖还是子依赖的依赖,都应该按照扁平化原则,优先将其放置在node_modules根目录。
npm缓存机制
-
介绍:在开发过程中,我们会多次的卸载和安装各种依赖包,导致了大量的网络请求。npm的缓存机制就是用来减少网络请求、加速重复安装过程。
-
查看与清除npm缓存
# 查看npm缓存
npm config get cache
# 清除npm缓存
npm cache clean --force
- _cacache文件夹目录
-
content-v2:里面基本都是一些二进制文件。为了使这些二进制文件可读,我们把二进制文件的扩展名改为.tgz,然后进行解压,得到的结果其实就是我们的npm包资源。
-
index-v5:里面基本都是一些二进制文件。将二进制文件的扩展名改为 .tgz,然后进行解压,就可以获得一些描述性的文件,这些内容就是content-v2里文件的索引。
-
tmp
- 缓存如何存储并利用
-
npm install执行时,通过pacote把相应的包解压在对应的node_modules下。npm 在下载依赖时,先下载到缓存当中,再解压到项目 node_modules 下。(pacote 是一个专为Node.js设计的库,用于下载npm兼容的包。)
-
pacote依赖npm-registry-fetch来下载包,npm-registry-fetch可以通过设置 cache属性,在给定的路径下生成缓存数据。
-
在每次安装资源时,根据package-lock.json中存储的integrity、version、name信息生成一个唯一的key,这个key能够对应到index-v5目录下的缓存记录。
-
如果发现有缓存资源,就会找到tar包的hash,根据hash再去找缓存的tar包,并再次通过pacote把对应的二进制文件解压到相应的项目node_modules下。
注: npm v5版本之前:~/.npm 文件夹中以模块名的形式直接存储,储存结构是:{cache}/{name}/{version}
npm小技巧
- npm init命令:输出一个初始化的package.json文件
# 自定义npm init命令
# .npm-init/init.js文件
#!/usr/bin/env node
module.exports = {
key: 'value'
name: prompt('name?', process.cwd().split('/').pop()),
version: prompt('version?' '0.0.1'),
description: prompt('请输入项目描述','项目描述...'),
main: 'index.js'
repository: prompt('github repository url', '', function(url){
if(url) {
run('touch README.md');
run('git init');
run('git add README.md');
run('git commit -m "first commit"');
run(`git remote add origin ${url}`);
run ('git push -u origin master');
}
return url;
})
}
npm config set init-module ./.npm-init/init.js
npm config set init.author.name ""
npm config set init.author.email ""
npm config set init.author.url ""
npm config set init.license "MIT"
- npm link命令:高效率在本地调试已验证包的可用性。npm link 的本质就是软链接,主要做了两件事:
-
为目标npm模块创建软链接,将其链接到全局node模块安装路径/usr/local/lib/node_modules/中。
-
为目标npm模块的可执行bin文件创建软链接将其链接到全局node命令安装路径/usr/local/bin/中。
- npx
-
可以直接执行node_modules/.bin文件夹下的文件、可以自动去node_modules/.bin 路径和环境变量$PATH里面检查命令是否存在。
-
npx执行模块时会优先安装依赖,但是在安装执行后便删除此依赖,这就避免了全局安装模块带来的问题。如:npx create-react-app react-project
npm镜像源管理工具
- 镜像源切换
# 切换至淘宝镜像源
npm config set init.author.name ""npm config set registry http://registry.npm.taobao.org
# 查看镜像源使用状态
npm get registry
# 切回官方镜像源
npm config set registry http://www.npmjs.org
- 使用nrm(npm registry manager)的镜像源管理工具
# 全局安装nrm
npm install -g nrm
# 查看可选的源
nrm ls
# 切换源
nrm use taobao
# 增加源
nrm add <registry> <url> #reigstry为源名,url为源的路径
# 删除源
nrm del <registry>
# 测试相应源的响应时间
nrm test <registry>
- 通过npm-preinstall的钩子和npm脚本,在安装公共依赖前自动切换源
"scripts": {
"preinstall": "node ./bin/preinstall.js"
}
# preinstall.js
require(' child_process').exec('npm config get registry', function(error, stout, stderr) {
if (!stdout.toString().match(/registry\.x\/com/)) {
exec('npm config set @xscope:registry https://xxx.com/npm/')
}
})
部署一个私有的npm镜像
- 常用工具:nexus、verdaccio、cnpm
yarn
yarn介绍
-
背景:解决npm安装速度慢,稳定性差的问题。在npm还处于V3的时候,npm还没有package-lock.json文件,当时的npm安装速度慢,稳定性差,所以在2016年,Yarn就出现了。
-
优点:
-
速度快:Yarn通过并行化操作大大提升了依赖安装的速度。与npm串行安装不同,Yarn可以同时执行多个任务,从而更快速地完成依赖安装。
-
离线模式:Yarn支持离线模式,即使在没有网络的情况下,也能安装依赖包。Yarn会缓存下载过的每一个包,下一次安装时直接从缓存中读取,大大提升了安装速度。
-
确定性:Yarn使用yarn.lock文件锁定依赖包的版本,确保每次安装的依赖包版本一致,避免了“今天能用明天不能用”的情况。
-
采用扁平化的安装模式:将依赖包的不同版本,根据一定的策略,归结为单个版本,避免创建多个副本产生冗余。
- 常用基本使用
# 初始化项目
yarn init
# 安装依赖
# –-dev 安装到devDependencies下
yarn add <packageName>
# 移除依赖包
yarn remove <packageName>
# 更新目录下指定的依赖包
yarn upgrade <packageName>
# 在全局安装依赖包
yarn global add <packageName>
# 查看配置key的值
yarn config get key
# 设置配置项key的值
yarn config set key
# 查询所有依赖
yarn list
# 查看依赖包的信息
yarn info <packageName>
# 获取配置信息
yarn config list
yarn缓存
- yarn默认使用prefer-online模式,也就是优先使用网络数据,如果网络数据请求失败,再去请求缓存数据。
# 查看yarn的缓存目录
yarn cache dir
# 查看缓存的包文件
yarn cache list
# 清除缓存
yarn cache clean
yarn的安装理念
-
检查包(Checking):检查是否存在npm相关的一些文件,比如package-lock.json文件等,如果有会提醒用户,可能会产生冲突,还会有cup、os的检查。
-
解析包(Resolving):解析依赖树中的所有包的版本信息。由于依赖树是一个多层嵌套的结构,所有在这一步会进行一个递归解析。
-
获取包:包资源的来源有两个地方,一个是网络,一个是缓存。Yarn如何区分我们的包是要在网络上下载还是从缓存中进去查找了?
- yarn会根据cacheFolder+slug+node_modules+pkg.name生成一个path;
- 判断系统中是否存在这个路径,如果存在,说明系统中存在缓存,不需要重新下载,如果不存在就需要重新下载
-
链接包:这一步的目的是将依赖资源包放入到项目的node_modules中。
-
构建包:如果依赖资源包存在二进制包需要编译,会在这一步进行处理。
pnpm
pnpm介绍
-
pnpm(Performant npm),速度快、节省磁盘空间的软件包管理器。
-
官网地址:https://www.pnpm.cn/
-
优势:
- 快速:pnpm比npm快了近2倍;
- 高效:node_modules中的所有文件均克隆或硬链接自单一存储位置;
- 支持单体仓库:pnpm内置了对单个源码仓库中包含多个软件包的支持;
- 权限严格:pnpm创建的node_modules默认并非扁平结构,因此代码无法对任意软件包进行访问
- 常用基本使用
# 安装软件包及其依赖的任何软件包
# 默认保存到dependencies
# -D:保存到 devDependencies
# -O:保存到 optionalDependencies
# -g:全局安装
pnpm add
# 用于安装项目所有依赖
pnpm install
# 根据指定的范围更新软件包的最新版本
pnpm update
# 从node_modules和项目的package.json中删除相关packages
pnpm remove
pnpm缓存
# 查看pnpm的缓存路径
# 您可以手动删除该路径下的文件,以清除pnpm的缓存
pnpm store path
# 清除缓存
pnpm store prune
pnpm的原理
- 硬连接(hard link):电脑文件系统中的多个文件平等地共享同一个文件存储单元
- 硬链接不会新建inode(索引节点),源文件与硬链接指向同一个索引节点;
- 硬链接不支持目录,只支持文件级别,也不支持跨分区;
- 删除源文件和所有硬链接之后,文件才真正被删除。
- 软连接(soft link):符号链接(软链接、Symbolic link)是一类特殊的文件,可以理解成快捷方式;
- 符号链接中存储的是源文件的路径,指向源文件,类似于Windows的快捷方式;
- 符号链接支持目录与文件,它与源文件是不同的文件,inode值不一样,文件类型也不同,因此符号链接可以跨分区访问;
- 删除源文件后,符号链接依然存在,但是无法通过它访问到源文件。
- pnpm首先将依赖安装到全局store,然后通过symbolic link和hard link来组织目录结构,将全局的依赖链接到项目中,将项目的直接依赖链接到node_modules的顶层,所有的依赖则平铺于node_modules/.pnpm目录下,实现了所有项目的依赖共享store的全局依赖,解决了幽灵依赖和NPM分身的问题
- 如果你对同一依赖包使用相同的版本,那么磁盘上只有这个依赖包的一份文件;
- 如果你对同一依赖包需要使用不同的版本,则仅有版本之间不同的文件会被存储起来;
- 所有文件都保存在硬盘上的统一的位置,当安装软件包时,其包含的所有文件都会硬链接到此位置,而不会占用额外的硬盘空间,这让你可以在项目之间方便地共享相同版本的依赖包;
npm、pnpm、yarn对比
特性 | NPM | Yarn | PNPM |
---|---|---|---|
发布时间 | 2010年 | 2016年 | 2017年 |
工作原理 | 递归地安装所有依赖到node_modules目录下 | 构建扁平化的依赖树并优化安装过程 | 使用单个全局存储层和符号链接来减少磁盘空间占用 |
安装方式 | npm install | yarn add/install | pnpm install |
缓存机制 | 本地缓存每个项目的依赖 | 共享缓存,保证多个项目间的依赖版本一致 | 共享存储层缓存,避免重复下载 |
安装速度 | 相对较慢,尤其在网络不佳时 | 快于npm,支持并行安装 | 更快,充分利用磁盘空间和网络资源 |
磁盘空间占用 | 每个项目下的node_modules有完整副本,占用较多空间 | 同一依赖只在全局缓存一份 | 利用符号链接进一步减少磁盘占用 |
离线模式 | 支持,但需先下载过相关包 | 供强大的离线模式 | 也支持离线安装 |
网络策略 | 单线程请求 | 多线程并行请求 | 多线程并行请求,智能网络调度 |
锁定文件 | package-lock.json | yarn.lock | pnpm.lock.yaml |
依赖一致性 | 通过package-lock.json实现一定的依赖版本控制 | 强制一致,每次安装都会严格按照yarn.lock文件 | 同样强制一致,基于pnpm-lock.yaml |
磁盘空间使用 | 重复存储包,空间利用率低 | 缓存机制较好 | 通过硬链接节省空间 |
安全性 | 通过npm audit检查依赖的安全性 | 同样具有审计功能,检查安全漏洞 | 支持安全审计 |
特色功能 | npx,用于执行一次性命令;npm ci用于快速、可靠的持续集成环境构建 | 更加严格的版本控制,改进的冲突解决策略;workspaces支持多包管理 | 针对monorepo设计,高效的多包管理和tree shaking能力 |
生态系统 | 最大,兼容性最好 | 兼容npm的所有特性,并额外增强了一些功能 | 对npm生态完全兼容,但在大型项目中有更好表现 |
依赖一致性 | 通过package-lock.json实现一定的依赖版本控制 | 强制一致,每次安装都会严格按照yarn.lock文件 | 同样强制一致,基于pnpm-lock.yaml |
磁盘空间使用 | 重复存储包,空间利用率低 | 缓存机制较好 | 通过硬链接节省空间 |
安全性 | 通过npm audit检查依赖的安全性 | 同样具有审计功能,检查安全漏洞 | 支持安全审计 |
特色功能 | npx,用于执行一次性命令;npm ci用于快速、可靠的持续集成环境构建 | 更加严格的版本控制,改进的冲突解决策略;workspaces支持多包管理 | 针对monorepo设计,高效的多包管理和tree shaking能力 |
生态系统 | 最大,兼容性最好 | 兼容npm的所有特性,并额外增强了一些功能 | 对npm生态完全兼容,但在大型项目中有更好表现 |