Skip to content

[toc]

1、Monorepo 介绍

Monorepo 是一种项目代码管理方式,指单个仓库中管理多个项目,有助于简化代码共享、版本控制、构建和部署等方面的复杂性,并提供更好的可重用性和协作性。

2、Monorepo 演进

阶段一:单体仓库 (Monolith)

特点:一个仓库包含所有代码

问题:

  • 代码量增大导致复杂度上升

  • 构建效率下降

  • 形成"巨石应用"

阶段二:多仓库 (MultiRepo)

特点:按业务拆分为多个独立仓库

优势:

  • 模块解耦

  • 独立开发、测试和发版

  • 提升构建效率

阶段三:单体多模块 (MonoRepo)

特点:多个项目模块统一在一个仓库管理

优势:

  • 统一工程配置

  • 便于代码共享

  • 简化依赖管理

  • 解决了 MultiRepo 的问题:

  • 跨仓库代码共享困难

  • 模块依赖管理复杂

  • 多仓库构建耗时

img

场景MultiRepoMonoRepo
代码可见性✅ 代码隔离,研发者只需关注自己负责的仓库 ❌ 包管理按照各自owner划分,当出现问题时,需要到依赖包中进行判断并解决。✅ 一个仓库中多个相关项目,很容易看到整个代码库的变化趋势,更好的团队协作。 ❌ 增加了非owner改动代码的风险
依赖管理❌ 多个仓库都有自己的 node_modules,存在依赖重复安装情况,占用磁盘内存大。✅ 多项目代码都在一个仓库中,相同版本依赖提升到顶层只安装一次,节省磁盘内存,
代码权限✅ 各项目单独仓库,不会出现代码被误改的情况,单个项目出现问题不会影响其他项目。❌ 多个项目代码都在一个仓库中,没有项目粒度的权限管控,一个项目出问题,可能影响所有项目。
开发迭代✅ 仓库体积小,模块划分清晰,可维护性强。 ❌ 多仓库来回切换(编辑器及命令行),项目多的话效率很低。多仓库见存在依赖时,需要手动 npm link,操作繁琐。 ❌ 依赖管理不便,多个依赖可能在多个仓库中存在不同版本,重复安装,npm link 时不同项目的依赖会存在冲突。✅ 多个项目都在一个仓库中,可看到相关项目全貌,编码非常方便。 ✅ 代码复用高,方便进行代码重构。 ❌ 多项目在一个仓库中,代码体积多大几个 G,git clone时间较长。 ✅ 依赖调试方便,依赖包迭代场景下,借助工具自动 npm link,直接使用最新版本依赖,简化了操作流程。
工程配置❌ 各项目构建、打包、代码校验都各自维护,不一致时会导致代码差异或构建差异。✅ 多项目在一个仓库,工程配置一致,代码质量标准及风格也很容易一致。
构建部署❌ 多个项目间存在依赖,部署时需要手动到不同的仓库根据先后顺序去修改版本及进行部署,操作繁琐效率低。✅ 构建性 Monorepo 工具可以配置依赖项目的构建优先级,可以实现一次命令完成所有的部署。

3、Monorepo 选型

3.1、构建型 Monorepo 方案

3.1.1、Turborepo

Turborepo 是 Vercel 团队开源的高性能构建代码仓库系统,允许开发者使用不同的构建系统。

3.1.2、Rush

Rush

早期,只提供了 Rush 作为构建调取器,其余事项交给用户灵活的选择任意构建工具链,由于过于灵活带来了很大的选型及维护成本,后来成立了 Rush Stack 来提供了一套可复用的解决方案,涵盖多项目的构建、测试、打包和发布,实现了更强大的工作流。

Rush 功能列举

  • 解决了幽灵依赖:将项目所有依赖都安装到 Repo根目录的common/temp下,通过软链接到各项目,保证了 node_modules 下依赖与 package.json 一致

  • 并行构建:Rush 支持并行构建多个项目,提高了构建效率

  • 插件系统:Rush 提供了丰富的插件系统,可以扩展其功能,满足不同的需求,具体参考

  • 项目发布,ChangeLog 支持友好:自动修改项目版本号,自动生成 ChangeLog

3.1.3、Nx

Nx 简介

构建加速思路(比 Turborepo 更丰富)

  • 缓存: 通过缓存 及 远程缓存,减少构建时间(远程缓存:Nx 公开了一个公共 API,它允许您提供自己的远程缓存实现,Turborepo 必须使用内置的远程缓存)
  • 增量构建: 最小范围构建,非全量构建
  • 并行构建: Nx 自动分析项目的关联关系,对这些任务进行排序以最大化并行性
  • 分布式构建: 结合 Nx Cloud,您的任务将自动分布在 CI 代理中(多台远程构建机器),同时考虑构建顺序、最大化并行化和代理利用率

3.2、轻量化 Monorepo 方案

3.2.1、Lerna

Lerna

Lerna是什么?

  • Lerna 是 Babel 为实现 Monorepo 开发的工具;最擅长管理依赖关系和发布
  • Lerna 优化了多包工作流,解决了多包依赖发版手动维护版本等问题
  • Lerna 不提供构建、测试等任务,工程能力较弱,项目中往往需要基于它进行顶层能力的封装

Lerna 主要做三件事

  • 为单个包或多个包运行命令 (lerna run)
  • 管理依赖项 (lerna bootstrap)
  • 发布依赖包,处理版本管理,并生成变更日志 (lerna publish)

Lerna 自动检测发布,判断逻辑

  1. 校验本地是否有没有被 commit 内容?
  2. 判断当前的分支是否正常?
  3. 判断当前分支是否在 remote 存在?
  4. 判断当前分支是否在 lerna.json 允许的 allowBranch 设置之中?
  5. 判断当前分支提交是否落后于 remote

3.2.2、yarn/npm + workspace

yarn 1.x 及以上版本,新增 workspace 能力,不借助 Lerna,也可以提供原生的 Monorepo 支持,需要在根目录下 package.json 中,声明 workspace

json
{
  "private": true, // 必须是私有项目
  "workspaces": ["project1", "project2/*"]
}

3.2.3、Lerna + pnpm + workspace

pnpm 相比于 npm、yarn 的包管理器,优势如下,同理是 Lerna + yarn + workspace 优势:

  • 装包速度极快: 缓存中有的依赖,直接硬链接到项目的 node_module 中;减少了 copy 的大量 IO 操作
  • 磁盘利用率极高: 软/硬链接方式,同一版本的依赖共用一个磁盘空间;不同版本依赖,只额外存储 diff 内容
  • 解决了幽灵依赖: node_modules 目录结构 与 package.json 依赖列表一致