单仓库模块管理,可使用 lerna 进行包管理
monorepo 是一种将多个项目代码存储在一个仓库里的软件开发策略(“mono” 来源于希腊语 μόνος 意味单个的,而 “repo”,显而易见地,是 repository 的缩写)
monorepo 的优劣势
通过 monorepo 策略组织代码,您代码仓库的目录结构看起来会是这样:
.
├── lerna.json
├── package.json
└── packages/ # 这里将存放所有子 repo 目录
├── project_1/
│ ├── index.js
│ ├── node_modules/
│ └── package.json
├── project_2/
│ ├── index.js
│ ├── node_module/
│ └── package.json
...
乍看起来,所谓的 monorepo 策略就只是将不同项目的目录汇集到一个目录之下,但实际上操作起来所要考虑的事情则远比看起来要复杂得多。
优点
- 项目代码可集中进行管理,使用统一的构建工具。
代码重用将变得非常容易:由于所有的项目代码都集中于一个代码仓库,我们将很容易抽离出各个项目共用的业务组件或工具,并通过 TypeScript,Lerna 或其他工具进行代码内引用; - 依赖管理将变得非常简单:同理,由于项目之间的引用路径内化在同一个仓库之中,我们很容易追踪当某个项目的代码修改后,会影响到其他哪些项目。通过使用一些工具,我们将很容易地做到版本依赖管理和版本号自动升级;
- 代码重构将变得非常便捷:想想究竟是什么在阻止您进行代码重构,很多时候,原因来自于「不确定性」,您不确定对某个项目的修改是否对于其他项目而言是「致命的」,出于对未知的恐惧,您会倾向于不重构代码,这将导致整个项目代码的腐烂度会以惊人的速度增长。而在 monorepo 策略的指导下,您能够明确知道您的代码的影响范围,并且能够对被影响的项目可以进行统一的测试,这会鼓励您不断优化代码;
- 它倡导了一种开放,透明,共享的组织文化,这有利于开发者成长,代码质量的提升:在 monorepo 策略下,每个开发者都被鼓励去查看,修改他人的代码(只要有必要),同时,也会激起开发者维护代码,和编写单元测试的责任心(毕竟朋友来访之前,我们从不介意自己的房子究竟有多乱),这将会形成一种良性的技术氛围,从而保障整个组织的代码质量。
- 模块间调试方便、问题定位和修复相对容易
缺点
- 仓库体积大,对构建工具要求较高
- 对于公司级别的 monorepo 策略而言,需要专门的 VFS 系统,自动重构工具的支持:设想一下 Google 这样的企业是如何将十亿行的代码存储在一个仓库之中的?开发人员每次拉取代码需要等待多久?各个项目代码之间又如何实现权限管理,敏捷发布?
- 项目粒度的权限管理变得非常复杂:无论是 Git 还是其他 VCS 系统,在支持 monorepo 策略中项目粒度的权限管理上都没有令人满意的方案,这意味着 A 部门的 a 项目若是不想被 B 部门的开发者看到就很难了。
- 为了保证代码质量,对版本控制和 Git 工作流要求更高
- 新员工的学习成本变高:不同于一个项目一个代码仓库这种模式下,组织新人只要熟悉特定代码仓库下的代码逻辑,在 monorepo 策略下,新人可能不得不花更多精力来理清各个代码仓库之间的相互逻辑,当然这个成本可以通过新人文档的方式来解决,但维护文档的新鲜又需要消耗额外的人力;
和单页应用类似
| 比较维度 | Nx | Lerna | Npm Workspaces | Yarn Workspaces |
|---|---|---|---|---|
| 初衷 | 专注大型前端 Monorepo,内置任务调度和构建缓存 | 传统 Monorepo 工具,提供发布流程与版本管理 | npm 原生多包管理,降低配置成本 | 基于 Yarn,强调性能与依赖缓存 |
| 依赖管理 | 内置工作区依赖处理,支持受控版本 | 可配置 hoist/nohoist,需在 lerna.json 中手动设置 | 默认会将依赖提升到根目录,可用脚本自定义 | 同样会进行依赖提升,可配置 nohoist |
| 版本/发布 | 可结合 Nx CLI 或其他工具(如 release-it)来发布 | 内置版本管理与发包功能,支持自动打标签与语义化版本 | 多包版本独立,需手动执行 npm publish | 多包版本独立管理,可结合 Yarn CLI 或脚本完成发布 |
| 构建能力 | 内置统一任务调度与缓存,适合大型前端项目 | 无内置构建功能,主要依赖第三方脚本或配置 | 仅提供多包管理,不负责构建流程 | 同样不内置完整构建功能,偏重依赖管理 |
| 适用场景 | 大型、复杂 Monorepo,需要统一构建和高效任务编排 | 中大型多包项目,需自动化版本与灵活依赖管理 | 小型或中型多包项目,偏好使用 npm 工具链 | 中型或有性能需求的多包项目,偏好 Yarn 的依赖缓存与快速安装 |
| 入门难度 | 略高,需学习 Nx-specific 配置与 CLI | 中等,需要掌握命令行与 lerna.json 配置 | 低,与 npm 原生流程一致,几乎无需额外学习 | 低,与 Yarn 命令类似,但需熟悉 Yarn 的自定义配置 |
| 主要优势 | 任务图、构建缓存、可扩展性强,支持多框架(React 等) | 历史悠久,生态成熟,自动化发布、Changelog 生成等功能齐全 | 无需额外插件即可上手,原生支持 | 安装快(依赖缓存)、配置灵活,社区插件多 |
| 主要劣势 | 学习和配置成本较高,文档相对 Lerna 稍少 | 构建优化与任务编排不如 Nx | 无高级发布功能,需要外部工具配合 | 高级 Monorepo 功能不如 Nx/Lerna,需组合其他工具完成发布等 |