工程化管理工具¶
NPM 机制及原理¶
npm 安装机制及企业级私服原理
关于 npm 的问题¶
- 删除node_modules和lockfiles,再重新install,是否存在风险?
- 所有依赖都安装到dependencies,不区分 devDependencies 会有问题吗?
- 应用依赖公共库A和B,同时A也依赖B,那么库B会被多次安装或重复打包吗?
- 一个项目中,既用npm,又用Yarn,会引发什么问题?
- 是否应该提交lockfiles文件到代码仓库?
npm 的核心目标¶
- “Bring the best of open source to you, your team and your company.”
- “给你和你的团队、你的公司带来最好的开源库和依赖。”
npm的安装机制和背后思想¶
- 图示:npm-install
- 构建依赖树时,遵循扁平化原则:优先放置在node_modules根目录
npm 缓存机制¶
- 查看缓存配置:
npm config get cache
;(通常在:~/.npm/_cacache) - 清除缓存:
npm cache clean --force
-
打开 ~/.npm/_cacache
- content-v2:二进制文件
- index-v5:content-v2文件索引
- tmp
-
缓存如何被存储,并被利用?
- 1)npm install执行时,通过pacote把对应包解压到 node_modules 下
- 2)pacote依赖 npm-registry-fetch 来下载包,根据 RFC 7234 生成缓存数据
- 3)每次安装资源时,根据 package-lock.json 存储的 integrity、version、name 信息生成唯一的key
- 4)如果发现又缓存资源,就找到 tar 的hash,再次通过 pacote 把对应二进制解压到 node_modules 下
npm 不完全小技巧¶
- 自定义npm init:
npm config set init-module ~\.npm-init.js
- 利用 npm link,在本地项目调试验证包的可用性(类似软链接)
-
npx 的作用
- 可以直接执行 node_modues/.bin 文件架夹下的文件
- npx 执行模块时会优先安装依赖,但在安装完后,删除依赖,避免来全局安装带来的问题
npm 多源镜像和企业级部署私服原理¶
- 钩子:
"preinstall": "node ./bin/preinstall.js"
- preinstall.js:执行镜像源切换
// npm install 前,自动进行源切换
require('child_process').exec(
'npm config get registry',
function(error, stdout, stderr) {
if (!stdout.toString().match(/registry\.x\.com/)) {
exec('npm config set @xscope:registry https://xxx.com/npm/');
}
});
-
nrm
- npm 的镜像源管理工具
- 方便快速的镜像源之间切换
-
如何部署一个私有npm镜像?
- nexus
- verdaccio
- cnpm
.npmrc
文件优先级¶
- env 环境变量 > 项目级 > 用户级 > 全局级 > npm 内置
.npmrc
Yarn 理念¶
Yarn 的安装理念及如何破解依赖管理困境
yarn 解决的问题¶
-
确定性
- yarn.lock 保证不管安装顺序如何,相同的依赖关系在不同环境安装时不变;npm 做不到。
-
采用模块扁平安装:避免造成冗余
- 网络性能更好:并发连接池排队
- 缓存机制,实现离线模式
yarn vs npm¶
-
synp 工具
- 将yarn.lock 转换为package-lock.json
-
yarn cache dir
- 查看缓存目录及内容
-
yarn 独有
- yarn import
- yarn license
- yarn pack
- yarn why
- yarn autoclean
-
npm 独有
- npm rebuild
Yarn 安装过程¶
-
1)检测包(checking)
- 检测项目中是否存在一些npm相关文件
- 检测系统 OS、CPU 等信息
-
2)解析包(resolving packages)
- 获取当前项目中package.json定义;
- 采取 遍历首层依赖 的方式获取依赖包的版本信息;
-
递归解析依赖
- 若没有解析过包 A,尝试从
yarn.lock
中获取到版本信息,并标记为已解析 - 如果在
yarn.lock
没找到包 A,则向 Registry 发起请求,获取满足版本范围内已知最高版本包信息,获取后将包标记为已解析
- 若没有解析过包 A,尝试从
-
3)获取包(fetching packages)
- 检查缓存中是够存在当前依赖包;
- 将缓存中不存在的依赖包下载到缓存目录,
yarn cache dir
。 -
如何判断缓存中是否存在当前依赖包?
- Yarn 根据
cacheFolder+slug+node_modules+pkg.name
生成一个 path; - 若存在,则命中缓存。
- Yarn 根据
-
4)链接包(linking packages)
- 将项目中的依赖复制到 node_modules 下,遵循扁平化原则
- 解析peerDependencies -> 出现冲突(则提示) -> 扁平化依赖树 -> 执行拷贝任务到node_modules下
-
5)构建包(building packages)
- 若依赖包中存在二进制包,进行编译
破解依赖管理困境¶
-
如何理解“嵌套地狱”?
- 项目依赖树的层级非常深,不利于调试和排查问题
- 依赖树的不同分支里,可能存在同样版本的相同依赖
- npm 包的安装顺对于依赖树的影响很大
-
方法一:删除 node_modules,重新安装,利用 npm 依赖分析能力,得到一个更清爽的结构
- 方法二:使用 npm dedupe 命名
- 方法三:Yarn 会自动执行 dedupe 命令
NMP CI¶
CI 环境上的 npm 优化及更多工程化问题分析
npm ci vs install¶
- npm ci 要求必需有 package-lock.json 或 npm-shrinkwrap.json 文件存在
- npm ci 会检测如果 node_modules 已经存在,则先删除再进行安装操作
- 如果 lock 与 package.json 中版本不匹配,npm ci 直接报错中断,而不是更新 lock 文件
- npm ci 不能用来安装单个依赖,只能用来安装整个项目的依赖
- 不会更新 package.json 或 package-lock.json 文件,整个安装过程是锁死的
- 缓存
npm ci --cache .npm
- npm ci 时建议加上
--quiet --no-progress
关闭进度和其他无用 log,否则产生的日志会很大。 - 所以 ci 时推荐完整的命令为
npm ci --cache .npm --quiet --no-progress
why lockfiles¶
- 问题:为什么要lockfiles,要不要提交lockfiles?
- package-lock.json 文件所用时锁定依赖安装结构;保证任意机器安装完全相同node_modules
- 单一package.json不能确定唯一依赖树;npm install 根据package.json中的 semver-range version 更新依赖
- 并非所有子依赖都有 dependencies属性,只有子依赖的依赖和当前已安装在根目录的 node_modules 中的依赖冲突后,才会有这个属性。
- 如果开发完整应用,建议提交 lockfiles;
- 如果开发发布类库,不提交 lockfiles,减少依赖重复和体积。
依赖类型声明¶
- dependencies 项目依赖
-
devDependencies 开发依赖
- 也可能被打包
-
peerDependencies 同版本依赖
- 插件安装时,依赖的核心库必须先下载安装
-
bundleDependencies 捆绑依赖
- optionalDependencies 可选依赖
最佳实践¶
- 使用 npm v5.4.2 之后的版本,保证先进性和稳定性
- 依靠 npm update 升级最新小版本
- npm install
@ 升级大版本 - 如果 package-lock.json 冲突,建议删除本地,拉取远程,重新 npm install
主流构建工具¶
横向对比主流构建工具,了解构建工具的设计考量
主流构建工具¶
- browserify
-
parcel
- 零配置,开箱即用
- 内置多核并行构建、及文件系统缓存
-
rollup
-
webpack
- 依赖plugin和loader,功能强大
- 监听文件增量构建,初始构建时间过长
Tooling.Report¶
- 构建工具对比平台
-
核心指标
-
Code splitting
- webpack/rollup表现最好
- 代码分割,能够导出公共模块,避免重复打包;
- 以及在页面加载允许时,实现最合理的按需加载策略
- 直接影响来前端项目性能
-
Hashing
- 表现一致
- 对打包资源进行版本信息映射,重点时最大化利用缓存机制
- 根据各模块依赖关系,支持开发者自定义哈希策略
-
webpack中的哈希策略
- hash:反映项目的构架版本,同一次构建,各文件hash一致
- chunkhash:会根据入口文件(Entry),进行依赖分析
- contenthash:会根据文件内容,生成hash值
-
Importing modules
- 表现一致
- 模块依赖机制
-
Non-JavaScript resources
- parcel 表现最好
- 非JavaScript类型资源的支持功能
-
Output module formats
- parcel/rollup表现最好
- 构建输出内容的模块化方式
-
Transformations
- parcel表现最好
- 对于类似 JSX 、vue 文件的编译,不会内置都构建工具中;而是利用Babel等社区能力
-
Vite¶
Vite 实现:从源码出发,构建bundleless开发工程
webpack 不足¶
vite 设计哲学¶
- 理念:Bundleless
- 开发环境:利用 浏览器 去解析 imports,在 服务端 按需编译返回,完全跳过打包这个概念(bundless)。
- 生成环境:利用 rollup 打包
- 源码解读