什么是 monorepo
概念
monorepo 是一种项目代码管理方式,指单个仓库中管理多个项目,有助于简化代码共享、版本控制、构建和部署等方面的复杂性,并提供更好的可重用性和协作性
演变
- monolith(单仓库单应用)
单仓库单应用,一个仓库维护着项目代码,随着业务迭代,项目越来越复杂越来越庞大,构建效率也会越来越低
- multirepo(多仓库多应用)
多仓库多应用,微前端就是这种形式,每个模块都可以独立编码、测试、上线,代码管理变得简化,构建效率也有很大提升
- monorepo(单仓库多应用)
单仓库多应用,多个项目集成到一个仓库下,共享工程配置,同时又快捷地共享模块代码
使用 monorepo 结构的优劣势
中大型项目,多个模块之间关联性较强,更适合用 monorepo 方式管理代码
相关基建
- 依赖管理
基于 workspace 实现依赖管理,首先公共依赖提升,节省空间和下载速度,其次依赖实时更新模块之间的调试更加方便
/* 配置 private */
{
"name": "pnpm-mono",
"version": "1.0.0",
"description": "",
"main": "index.js",
"private": true,
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
}
// pnpm-workspace.yaml 文件配置
packages:
- packages/*
packages 目录下创建 UI、components 两个应用
// components 中安装依赖 ui
pnpm --filter @pnpmpkg/components add @pnpmpkg/ui -S
/* packages.json 中会添加相关依赖 */
"dependencies": {
"@pnpmpkg/ui": "workspace:^"
}
- 版本管理(eg:changeset)
自动发版、更新版本号,更新方式有独立更新、同步更新
/* 初始化 */
npx changeset init
/* 记录需要更新的包和版本号 */
npx changeset
/* 更新对应包的版本号 */
npx changeset version
- 打包构建(eg:pnpm)
对接 CICD,控制编排构建顺序,发布应用
/* --parallel 并行打包 -r 打包所有 package */
pnpm run --parallel -r build
/* 发布 package */
pnpm publish
- 增量构建 & 缓存(eg:turborepo)
使用缓存技术保存上次构建结果,包括编译后的代码、测试结果等,如果检测到某个包或任务自上次构建以来没有变化(基于文件指纹比较),跳过这些任务的执行,并直接使用缓存中的结果,只有自上次成功构建以来发生变化的部分才会被重新构建
// 安装 turborepo
pnpm add turbo -D
// 配置 turbo.json 文件
{
"$schema": "https://turbo.build/schema.json",
"pipeline": {
"build": {
"outputs": ["dist/**"]
},
"lint": {}
}
}
// 执行编译
npx turbo run build
实现方案
- 轻量化方案:npm、yarn、pnpm
对于依赖管理相关,pnpm 有自己独有的优势,比如幽灵依赖、下载速度、磁盘空间等,所以依赖管理的最优解还是 pnpm
- 构建性方案:pnpm + lerna + Nx、 pnpm + changeset + Turborepo
1、lerna 和 Nx 现在归一个公司管理,lerna 集成 Nx 更方便,二者结合打包速度更快( Nx 避免还原每个文件,而是检查实际需要从缓存中还原哪些文件,从而减少 I/O 负载, Turborepo 每次都会从缓存中完全恢复文件)
2、Turborepo 借鉴了 Nx 的许多想法,新出来的框架,社区贡献相对较少
- 方案建议
中型项目全功能轻量级方案:pnpm + changeset + Turborepo
中大型项目构建速度最优化方案:pnpm + lerna + Nx