link : https://rushjs.io/zh-cn/pages/intro/welcome/
rush管理monorepo理论及实践
2021-08-07 3,735
阅读4分钟What is monorepo
简单来说,就是一个git仓库管理某个范围的所有代码
Why monorepo
- 源码透明度
- 级联发布(如babel)
- 代码复用
- 配置复用(可hoist到上层,子项目继承)
- 强制沟通(如公共库只能提供最新版本,库开发者和使用者联系更紧密)
- ...
rush
简单来说,rush提供了大型仓库(如monorepo)的管理能力
同时rush支持底层使用pnpm进行包管理,解决Phantom dependency、NPM doppelgangers等问题
rush基本使用
rush init
将项目初始化为rush管理
rush update(在rush.json的目录或其任意子目录执行)
- 更新common/config
- 检查所有项目的package.json,对比仓库的common shrinkwrap文件,确定其是否有效,如果过期了则更新shrinkwrap文件
- 所有依赖都安装到common/temp/node_modules
- 最后,rush为每个项目创建一个node_modules目录,并创建symlinks软链到common/temp/node_modules
rush update可能更新lockfile,rush install则会按lockfile安装
rush update --purge(强制重新安装依赖而不是基于缓存)
什么是shrinkwrap文件(如package-lock.json)
通过在git管理的一个大文件中保存一份完整的依赖安装计划解决依赖安装的不确定性问题
rush add
安装依赖
rush add -p webpack --dev // -p指定要安装的包,这里将webpack安装为子项目dev依赖
rushx
相当于npm run
rush build(在rush.json的目录或其任意子目录执行)
rush build会执行所有项目npm script中的build
pnpm基本原理
依赖文件统一提升到上层,子项目中node_modules中使用软链依赖树(参考前文中rush update的依赖安装细节)
正确的依赖树结构(相较于扁平化依赖)确保了依赖使用的安全性(如项目中引入的库一定是package.json中所声明的依赖)
同时软链到上层统一管理的依赖,可以防止依赖的重复安装
Phantom dependency
简单来说,就是代码中可以加载到未在package.json中声明的依赖库
如A依赖B、C,同时B、C都依赖D
则npm会进行依赖扁平化来复用D,即安装后为B、C、D都在A的node_modules下最外层
现在A中可以直接依赖D,虽然D不在A的package.json声明的依赖中
影子依赖带来的具体问题
- 不兼容的版本
假设我们的代码依赖了A并指定为^3,A中依赖了B
当我们的代码直接引入B时,B的具体版本我们将一无所知,因为B的版本完全由A的开发者控制
假设A进行了一个patch升级,则仍符合我们指定的版本范围,但如果A中对B的版本指定为主版本的升级版本,则可能导致我们代码中引入的B出现不兼容
- 依赖丢失
假设我们作为库lib的开发者,lib中有一个dev依赖为A,A中依赖了B
当我们在代码中使用了B并发布出去,使用方install lib时不会安装lib的dev依赖,即不会安装A,则A的依赖B自然也不会安装
此时我们的库由于无法引入B而出现异常
同时,由于扁平化依赖的原因,可能使用方在安装其他库时也隐式安装了B,导致我们的库虽然缺失了B的依赖声明仍能正常工作
NPM doppelgangers
四个依赖A、B、C、D,其中A、B依赖E@1,C、D依赖E@2
则可能的情况为
- 先扁平化安装了E@1,则C和D的node_modules中都会安装E@2
- 先扁平化安装了E@2,则A和B的node_modules中都会安装E@1
影响
- 重复安装依赖(影响安装速度和磁盘空间)
- 影响打包体积(重复包都会打进来)
- 可能破坏三方库内部的单例模式(E中可能有些逻辑依赖单例,而重复的E被依赖时可能分别引入不同的实例)
- ts类型冲突
- ...
子项目间依赖
依赖方dependencies里指定workspace:*(pnpm提供workspace协议)
如子项目app依赖子项目libs,则在app的package.json中
"dependencies": {
"libs": "workspace:*"
}
构建产物依赖
前置build libs
rush build -T . // -T .表示构建当前子项目所有依赖项目(排除自身)
libs的package.json中main指向产物入口
{
"main": "./dist/main.js"
}
app引入libs时和普通三方库一致即可
源码依赖(两种方式)
- libs的package.json中main字段指定包入口,app直接引入
libs的package.json
{
"main": "./src/index.js"
}
app中引入
import * as libs from 'libs'
- 直接用相对路径引入
import * as libs from '../../libs/src'
标签:rush,依赖,package,实践,json,libs,安装,monorepo
From: https://www.cnblogs.com/gaoyanbing/p/18636518