monorepo

monorepo

一、Monorepo 介绍

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

二、Monorepo 演进

阶段一:单仓库巨石应用, 一个 Git 仓库维护着项目代码,随着迭代业务复杂度的提升,项目代码会变得越来越多,越来越复杂,大量代码构建效率也会降低,最终导致了单体巨石应用,这种代码管理方式称之为 Monolith。

阶段二:多仓库多模块应用,于是将项目拆解成多个业务模块,并在多个 Git 仓库管理,模块解耦,降低了巨石应用的复杂度,每个模块都可以独立编码、测试、发版,代码管理变得简化,构建效率也得以提升,这种代码管理方式称之为 MultiRepo。

阶段三:单仓库多模块应用,随着业务复杂度的提升,模块仓库越来越多,MultiRepo这种方式虽然从业务上解耦了,但增加了项目工程管理的难度,随着模块仓库达到一定数量级,会有几个问题:跨仓库代码难共享;分散在单仓库的模块依赖管理复杂(底层模块升级后,其他上层依赖需要及时更新,否则有问题);增加了构建耗时。于是将多个项目集成到一个仓库下,共享工程配置,同时又快捷地共享模块代码,成为趋势,这种代码管理方式称之为 MonoRepo。

三、Monorepo 优劣


四、Monorepo 场景

综合如上 Monorepo VS MultiRepo,中大型项目,多模块项目,更适合用 MonoRepo 方式管理代码,在开发、协作效率、代码一致性方面都能受益。

五、Monorepo 踩坑

5.1、幽灵依赖

问题:npm/yarn 安装依赖时,存在依赖提升,某个项目使用的依赖,并没有在其 package.json 中声明,也可以直接使用,这种现象称之为 “幽灵依赖”;随着项目迭代,这个依赖不再被其他项目使用,不再被安装,使用幽灵依赖的项目,会因为无法找到依赖而报错。
方案:基于 npm/yarn 的 Monorepo 方案,依然存在 “幽灵依赖” 问题,我们可以通过 pnpm 彻底解决这个问题

5.2、依赖安装耗时长

问题:MonoRepo 中每个项目都有自己的 package.json 依赖列表,随着 MonoRepo 中依赖总数的增长,每次 install 时,耗时会较长。
方案:相同版本依赖提升到 Monorepo 根目录下,减少冗余依赖安装;使用 pnpm 按需安装及依赖缓存。

5.3、构建打包耗时长

问题:多个项目构建任务存在依赖时,往往是串行构建 或 全量构建,导致构建时间较长。
方案:增量构建,而非全量构建;也可以将串行构建,优化成并行构建。

六、Monorepo 选型

6.1、构建型 Monorepo 方案

此类工具,主要解决大仓库 Monorepo 构建效率低的问题。项目代码仓库越来越庞大,工作流(int、构建、单元测试、集成测试)也会越来越慢;这类工具,是专门针对这样的场景进行极致的性能优化。适用于包非常多、代码体积非常大的 Monorepo 项目。

6.1.1、Turborepo

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

  • Multiple Running Task:构建任务并行进行,构建顺序交给开发者配置
  • Cache、Remote Cache:通过缓存 及 远程缓存,减少构建时间
    举例 Multiple Running Task:我们现在有一个 Monorepo 的项目,有以下几个 package:
  • apps/web,依赖 shared
  • apps/docs,依赖 shared
  • package/shared,被 web 和 docs 依赖
# 当我们使用正常的 yarn workspace 去管理 monorepo 的工作流任务时,例如执行以下命令:
yarn workspaces run lint
yarn workspaces run test
yarn workspaces run build

传统的 yarn workspace 问题:串行构建,性能差。
Turborepo Multiple Running Task:允许用户在 turbo.json 中声明 task 之间依赖关系,优化后构建。

6.1.2、Rush

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

  • Rush: 可扩展的 monorepo 构建编排工具
  • Heft: 可以与 Rush 交互的可扩展构建系统
  • API Extractor: 为工具库审阅 API 并生成 .d.ts 文件
  • API Documenter: 生成你的 API 文档站
  • @rushstack/eslint-config: 专门为大型 TypeScript monorepo 仓库设计的 ESLint 规则集
  • @rushstack/eslint-plugin-packlets: 可用于在单个项目内来组织代码,NPM 发包的一个轻量级解决方案
  • Rundown: 用于优化 Node.js 启动时间的工具

Rush 功能列举

  • 解决了幽灵依赖:将项目所有依赖都安装到 Repo根目录的common/temp下,通过软链接到各项目,保证了 node_modules 下依赖与 package.json 一致
  • 并行构建:Rush 支持并行构建多个项目,提高了构建效率
  • 插件系统:Rush 提供了丰富的插件系统,可以扩展其功能,满足不同的需求,具体参考
  • 项目发布,ChangeLog 支持友好:自动修改项目版本号,自动生成 ChangeLog

6.1.3、Nx

Nx 是 Nrwl 团队开发的,同时在维护 Lerna,目前 Nx 可以与 Learn 5.1及以上集成使用
构建加速思路(比 Turborepo 更丰富)

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

    用 Nx 强大的任务调度器加速 Lerna:Lerna 擅长管理依赖关系和发布,但扩展基于 Lerna 的 Monorepos 很快就会变得很痛苦,因为 Lerna 很慢。这就是 Nx 的闪光点,也是它可以真正加速你的 monorepo 的地方。

6.2、轻量化 Monorepo 方案

6.2.1、Lerna(全面讲解)

Lerna 是什么?

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

Lerna 主要做三件事

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

Lerna 能解决了什么问题?

  • 代码共享,调试便捷: 一个依赖包更新,其他依赖此包的包/项目无需安装最新版本,因为 Lerna 自动 Link
  • 安装依赖,减少冗余:多个包都使用相同版本的依赖包时,Lerna 优先将依赖包安装在根目录
  • 规范版本管理: Lerna 通过 Git 检测代码变动,自动发版、更新版本号;两种模式管理多个依赖包的版本号
  • 自动生成发版日志:使用插件,根据 Git Commit 记录,自动生成 ChangeLog

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

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

参考:

https://juejin.cn/post/7215886869199896637

评论

暂无

添加新评论