Monorepo
Why
当项目变得庞大的时候,自然就会想到去拆分,以更小的单位去维护,或者复用。
一种组织方式是,将项目拆成多个独立的 repo,然后通过子模块关联,
或者将某些模块发到 npm,通过 npm 去安装依赖。
这种拆分成多个 repo 的组织形式,称为 multirepo
,这种组织形式的有什么优劣呢?
multirepo 优缺点
优点
- 每个仓库的大小一般不会很大
- 每个仓库独立,可以单独对仓库进行权限控制, 例如控制谁有权限访问,谁可以提交等。
缺点
- 当维护多个 repo 时,需要在多个 repo 之间切换,容易混乱。
- 每个仓库可能有一套自己的配置,仓库之间不兼容,标准不统一。
- repo 和子模块可能存在重复的依赖,导致依赖的重复安装。
- 不在同一个 repo 中,不方便分析整个项目的所有依赖。
- 发布的时候,如果多个 repo 存在依赖关系,要按照依赖顺序依次构建再部署。
- repo 之间存在依赖关系,调试依赖的仓库时,需要 npm link 关联仓库。
What
面对 multirepo 的这些问题,就有人提出了 monorepo
的组织方式。
A monorepo is a single repository containing multiple distinct
projects, with well-defined relationships.
一个 monorepo 应该是:
- 单一的仓库
- 仓库中包含多个独立的项目
- 每个项目之间的关系定义清晰明确
简单的理解,monorepo 就是把原来的 multirepo 合并成一个 repo。
那是不是把每个 repo 分别放到不同的目录,建一个新的庞大的仓库,就完事了呢?
这样就变成一个“大杂烩”了,被称为 Single-repo Monolith
, 已经接近 monorepo 的样子了。
在这个基础上,还需要:
- 每个 repo 逻辑上独立,项目之间可能是不相关的,松散连接的,或者可以通过一些方式连接起来。(譬如 pnpm 的 workspace)
- 要保证项目之间的依赖关系定义清晰
- 项目间的依赖关系
- monorepo 的项目之间可能存在依赖关系,如何可以方便地添加依赖,完成构建。在 pnpm 中,可以在子项目的
package.json
中通过 workspace 关键字去引用项目。 - 项目第三方依赖间的关系
- monorepo 中可能多个项目同时依赖了相同的第三方依赖,如都依赖 vue。在 pnpm,相同的依赖可以考虑按照到 workspace 中,而不是具体的项目里。
尽管都是依赖 vue,但是不同项目可能依赖不同版本的 vue,需要解决版本问题。
对于 项目间的依赖关系 ,为了实现一定程度的自动化,需要借助一些工具,如 NX,Lerna,turborepo。
对于 项目第三方依赖间的关系, 可以借助 yarn,pnpm 等包管理工具的 workspace 功能去实现。
所以,有别于 Single-repo Monolisth, monorepo 需要有明确清晰的依赖关系,能够很方便地管理依赖。
monorepo 优缺点
优点
- 更容易管理依赖
- 源码都在一起,方便调试
- 可以在多个模块中,复用相同的配置,例如单元测试,CI/CD 相关的配置,便于保持规范一致
- 由于相关的项目都在一起,因此可以比较方便地编写集成测试
- 当有一个功能,牵涉多个模块时,可以在一个提交中改好,而 multirepo 则需要在多个 repo 做提交
缺点
- 项目启动,打包构建速度变慢
- 当项目很庞大,提交记录很多时,git 的一些操作性能上会变慢
- 没法限制不同模块的访问权限。也是一个优点,开发人员能看到所有模块,了解到这些模块的关联,而不是只关注自己的模块。
- 可能出现"幽灵"依赖,由于依赖安装在项目的 root 目录,所有模块能访问到,
于是即使模块中忘了声明某个依赖,但是 root 中存在,则可以使用。但实
际部署时,会因为没有声明依赖,导致没有安装而报错。
例子
- Monorepos By Example: Part 1
- 一个应用 monorepo 的例子,用 Lerna 管理依赖。
- Spike-Leung/leetcode
- 我自己的实践,分成了 solutions, solution-parser, web 三个 package,然后用 pnpm 的 workspace 关联。
- illa-design
- 一个组件库,组件之间就是以 monorepo 的形式组织的,目前使用的工具是 turborepo
- vuejs/core
- 看起来也是一个 monorepo 的组织方式,使用了 pnpm 去管理依赖
Refs
- monorepo.tools
- 对 monorepo 的整体介绍,同时比对了不同 monorepo 工具之间的优缺点
- Monorepo 的过去、现在、和未来
- 解释了 monolith 和 moporepo 间的差别,怎么实现这些差别,以及相关的工具,比较了作者自己用到的一些工具
- From many to one: Moving our JavaScript code into a monorepo
- Aha! 团队迁移 monorepo 的选型
- 开源项目都在用 monorepo,但是你知道居然有那么多坑么?
- 对比了 multirepo 和 monorepo 的优缺点,较详细地分析了 monorepo 带来的问题:依赖,构建,测试,发布等
- Guide to Monorepos for Front-end Code
- 讲了为什么要用 monorepo,monorepo 的优劣,以及实现 monorepo 的工具链
- Awesome Monorepo
- 整理了 monorepo 相关的工具
- JavaScript package managers compared: npm, Yarn, or pnpm?
- 比对了常用的包管理工具: npm vs yarn vs pnpm, 提及他们对 monorepo 的支持
- Exploring the Typescript Monorepo (a practical, hands-on adventure)
- Monorepo 引子,作为目录管理和 multirepo 管理的折中模式,同时包含一些实践。