1.什么是 Monorepo?为什么我们需要认真管理它?
Monorepo(单仓库多项目) 是一种将多个应用、库、服务或工具统一放在一个代码仓库中进行管理的开发模式。与传统的 Multi-repo(每个项目独立仓库)不同,Monorepo 允许团队在同一个代码库中协同开发前端应用、共享组件库、后端微服务甚至 CLI 工具。
这种模式在 Google、Meta、Microsoft 等大型科技公司早已广泛应用,近年来也逐渐被中小型团队采纳——因为它能显著降低协作成本、提升代码复用率、简化依赖管理和统一工程规范。
然而,Monorepo 的优势背后也隐藏着复杂性:
- 如何避免每次提交都触发全量构建和测试?
- 当 utils 库改动时,哪些应用会受到影响?
- 如何高效并行执行任务,并在 CI 中复用历史构建结果?
- 新成员如何快速创建符合规范的新项目?
这些问题如果靠脚本或人工维护,很快会陷入混乱。因此,我们需要一套专业的 Monorepo 管理工具——它不仅要支持增量构建、依赖分析、任务缓存,还要提供良好的开发者体验和对主流技术栈(如 React、TypeScript、Node.js)的深度集成。
✅ 本次调研的核心目标:评估当前主流 Monorepo 方案(Nx、Turborepo、Moon 等),判断是否有比 Nx 更适合我司场景的替代方案,或确认 Nx 是否仍是综合最优解。
2. 各个工具的功能鉴赏
2.1 Nx(当前参考基准)
- 依赖图分析:
nx graph可视化项目依赖。 - 智能缓存:基于源码哈希判断是否需重新构建,支持 Nx Cloud 远程缓存(付费)。
- 插件生态:官方支持 React、Vue、Angular、NestJS、Next.js 等;社区插件丰富。
- 代码生成:通过
nx g @nx/react:app my-app快速创建标准化项目。 - 任务管道:定义
targetDependencies实现任务依赖链。
2.2 Turborepo(Vercel 出品)
- 极简配置:通过
turbo.json定义 pipeline,学习成本低。 - 远程缓存免费:集成 Vercel Remote Caching,开源项目可免费使用。
- 无内置依赖图:需借助第三方工具(如
madge)分析依赖。 - 专注前端生态:对 Next.js / React 优化最好,其他技术栈支持较弱。
2.3 Lerna(传统方案)
- 包版本管理强项:
lerna version+lerna publish适合 npm 包发布流程。 - 缺乏增量构建:默认全量执行脚本,需配合
-since手动过滤。 - 已转向轻量模式:新版本移除内置 hoisting,推荐搭配 pnpm/yarn workspaces 使用。
2.4 Rush(微软出品)
- 企业级设计:强调确定性安装、变更日志管理、批量命令执行。
- 强 TypeScript 支持:自动链接类型定义,适合大型 TS 项目。
- 配置复杂:需编写
rush.json、common/config/rush/*等多文件。 - 依赖图支持:
rush graph可生成依赖关系图。
2.5 Moon(新兴高性能方案)
- 极速执行引擎:基于 Rust 编写,启动和任务调度速度显著优于 JS/TS 工具。
- 声明式配置:通过单一
moon.yml文件定义项目结构与任务流水线,简洁清晰。 - 多语言原生支持:无需插件即可管理 TypeScript、Rust、Python、Go 等项目。
- 内置缓存与远程执行:支持本地缓存 + 自建远程缓存服务器(无厂商锁定)。
- 依赖图可视化:提供
moon project graph命令生成依赖关系图。 - CI/CD 深度优化:
moon ci自动并行化、分片、缓存复用,适合大型流水线。 - 无代码生成器:当前版本不支持类似 Nx 的 scaffolding 功能,需手动创建项目模板。
💡 Moon 定位为“现代 Monorepo 的高性能替代方案”,强调确定性、可扩展性和开发者效率。
3. 使用的核心流程
| 工具 | 初始化 Monorepo | 添加新应用/库 | 构建指定项目 | 增量测试 | 查看依赖图 |
|---|---|---|---|---|---|
| Nx | npx create-nx-workspace@latest | nx g @nx/react:lib ui | nx build my-app | ✅ 自动跳过未变更项目 | nx graph |
| Turborepo | npx create-turbo@latest | 手动创建目录 + package.json | turbo run build --filter=my-app | ✅ 依赖 pipeline 配置 | ❌(需第三方) |
| Lerna | lerna init + 配合 pnpm/yarn | 手动创建包 + lerna create(旧版) | lerna run build --scope=my-app | ⚠️ 需 --since 手动指定 | ❌ |
| Rush | rush init | rush add -p my-lib --make-consistent | rush build --to my-app | ✅ 基于变更集 | rush graph |
| Moon | pnpm create moon | 手动创建目录 + 在 moon.yml 中注册 | moon run web:build | ✅ 自动识别依赖变更 | moon project graph |
💡 注:所有工具均需配合包管理器(如 pnpm workspace 或 yarn workspaces)实现 node_modules 共享。
4. 测试流程 & 结果
4.1 Turborepo 测试流程
步骤 1:初始化项目结构(复用 Nx 创建的目录)
# 在已有 monorepo-test 目录中操作
cd monorepo-test
pnpm add -D turbo
步骤 2:配置 turbo.json
{
"$schema": "<https://turbo.build/schema.json>",
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
},
"test": {
"dependsOn": ["^build"],
"outputs": []
}
}
}
⚠️ 注意:Turborepo 不会自动识别项目依赖,必须通过 package.json 中的 dependencies 显式声明(如 web 依赖 @myorg/utils)。
步骤 3:为每个子包添加脚本
-
apps/web/package.json:{ "scripts": { "build": "vite build" } } -
libs/utils/package.json:{ "scripts": { "build": "tsc", "test": "jest" } }
步骤 4:执行命令测试
| 功能 | 命令 | 结果 |
|---|---|---|
| 构建 web | pnpm turbo run build --filter=web | 成功,自动构建 utils(因依赖关系) |
| 增量缓存 | 再次运行相同命令 | 输出 cached (local),命中缓存 |
| 查看依赖逻辑 | pnpm turbo run build --dry-run | 打印任务执行顺序(但无图形化) |
Turborepo 缓存命中日志截图
✅ 优势 vs Nx:
- 配置极简:仅需
turbo.json+ 标准 npm scripts。 - 远程缓存免费:Vercel 提供开箱即用的 Remote Caching(开源项目免费)。
- 构建速度更快:任务调度更轻量,尤其适合纯前端栈(React/Vite/Next.js)。
❌ 不足 vs Nx:
- 无内置依赖图:无法直观查看
web → utils关系,调试依赖需靠-dry-run或第三方工具。 - 无代码生成器:创建新库需手动配置路径别名、tsconfig、package.json。
- 弱多技术栈支持:对非 JS/TS 项目(如 Go、Python 微服务)几乎无支持。
4.2 Lerna 测试流程
步骤 1:安装并初始化
pnpm add -D lerna
npx lerna init
⚠️ Lerna 新版本已移除 packages/ 自动 hoisting,需配合 pnpm workspace 使用。
步骤 2:确保包结构正确
packages/下包含web和utils(需手动移动或创建)- 每个包有独立
package.json,且web声明依赖"@myorg/utils": "workspace:*"
步骤 3:执行命令
| 功能 | 命令 | 结果 |
|---|---|---|
| 构建所有 | pnpm exec lerna run build | 全量执行,即使未变更 |
| 增量构建 | pnpm exec lerna run build --since HEAD~1 | 仅构建 git diff 涉及的包 |
| 运行测试 | pnpm exec lerna run test --scope=@myorg/utils | 可指定范围,但无智能跳过 |
📌 插图位置:Lerna 全量构建日志 vs 增量构建日志对比
全量构建日志
增量构建日志
✅ 优势 vs Nx:
- npm 包发布流程成熟:
lerna version+lerna publish是行业标准。 - 轻量无侵入:不强制项目结构,适合已有仓库渐进迁移。
❌ 不足 vs Nx:
- 无增量缓存机制:每次构建都重新执行,CI 耗时长。
- 无依赖感知:
-since仅基于 git,无法理解“间接依赖变更”(如 utils 改了,web 应重测)。 - 无可视化/代码生成:纯 CLI 工具,开发体验原始。
4.3 Rush 测试流程
步骤 1:初始化
pnpm create @microsoft/rush
rush update # 安装依赖
⚠️ Rush 强制使用自己的依赖安装机制(不直接兼容 pnpm install)。
步骤 2:添加项目
rush add -p @myorg/utils --make-consistent
实际仍需手动创建 apps/web 和 libs/utils 目录,并配置 rush.json 中的 projects 字段。
步骤 3:配置构建脚本
- 在
common/config/rush/command-line.json中定义自定义命令(如build)。 - 每个项目需有
package.json中的对应 script。
步骤 4:执行命令
| 功能 | 命令 | 结果 |
|---|---|---|
| 构建 web 及依赖 | rush build --to web | 成功,自动构建 utils |
| 查看依赖图 | rush graph | 生成 .svg 文件,可查看依赖关系 |
| 增量测试 | 修改 utils 后运行 rush rebuild | 仅重构建受影响项目 |
Rush 生成的依赖图 SVG 截图
✅ 优势 vs Nx:
- 强 TypeScript 集成:自动处理类型链接,避免
paths配置错误。 - 企业级确定性:严格控制 node_modules 结构,避免“在我机器上能跑”问题。
- 变更集管理:支持
rush change生成 changelog,适合合规团队。
❌ 不足 vs Nx:
- 配置复杂:需维护
rush.json、command-line.json、version-policies.json等多个配置文件。 - 学习曲线陡峭:概念多(如 “phased commands”, “variants”),新人上手慢。
- 生态封闭:插件少,社区活跃度远低于 Nx/Turborepo。
4.4 综合对比表
| 能力 | Nx | Turborepo | Lerna | Rush |
|---|---|---|---|---|
| 增量构建 | ✅ 智能依赖感知 | ✅(需正确配置 outputs) | ⚠️ 仅 git diff | ✅ 基于变更集 |
| 本地缓存 | ✅ | ✅ | ❌ | ✅ |
| 远程缓存 | ✅(Nx Cloud 付费) | ✅(Vercel 免费) | ❌ | ❌(需自建) |
| 依赖图可视化 | ✅ 内置交互式 | ❌ | ❌ | ✅(生成 SVG) |
| 代码生成器 | ✅ 丰富插件 | ❌ | ❌(旧版有) | ❌ |
| 多技术栈支持 | ✅(Go/Java/Python 等) | ❌(仅 JS/TS) | ⚠️(仅 npm 脚本) | ⚠️(主攻 TS) |
| 配置复杂度 | 中 | 低 | 低 | 高 |
| 适合场景 | 复杂企业 Monorepo | React/Next.js 快速开发 | 纯 npm 包发布 | 超大型 TS 项目 |
注意:
- 若追求 功能完整性 + 长期可维护性 → Nx 仍是首选。
- 若团队 专注 React/Next.js + 追求极致构建速度 → Turborepo 值得试点。
- Lerna 仅推荐用于包发布,Rush 适合微软级 TS 项目,一般团队慎选。
4.4 Moon 测试流程
步骤 1:初始化 Moon 项目
# 在干净目录中初始化
pnpm create moon
cd monorepo-moon-test
Moon 默认集成 pnpm workspace,并生成基础目录结构。
步骤 2:配置项目结构
手动创建:
apps/web/libs/utils/
并在根目录 moon.yml 中注册:
# moon.yml
projects:
sources:
- 'apps/*'
- 'libs/*'
tasks:
build:
command: 'vite build' # apps/web 使用
inputs: ['src/**']
outputs: ['dist/**']
build: # libs/utils 使用(覆盖)
command: 'tsc'
inputs: ['src/**']
outputs: ['lib/**']
⚠️ Moon 允许按项目类型覆盖任务定义,或使用 platforms 区分语言。
步骤 3:配置子包 package.json
-
apps/web/package.json:{ "name": "web", "dependencies": { "@myorg/utils": "workspace:*" } } -
libs/utils/package.json:{ "name": "@myorg/utils" }
Moon 会自动解析 dependencies 推断项目间依赖。
步骤 4:执行命令测试
| 功能 | 命令 | 结果 |
|---|---|---|
| 构建 web | moon run web:build | 成功,自动先构建 utils |
| 增量缓存 | 再次运行相同命令 | 输出 Cache hit,跳过执行 |
| 查看依赖图 | moon project graph | 生成交互式 HTML 或 PNG 图(取决于配置) |
📌 Moon 依赖图
✅ 优势 vs Nx:
- 性能卓越:Rust 引擎在大型仓库中任务调度更快。
- 缓存自由:远程缓存可自建 HTTP 服务,无 SaaS 绑定。
- 多语言友好:轻松混合 TS + Rust + Python 项目,无需额外插件。
- CI 开箱即用:
moon ci自动优化并行与缓存恢复。
❌ 不足 vs Nx:
- 无代码生成器:无法一键生成标准化 React 应用或库。
- 生态较新:社区小,VS Code 插件、调试工具等支持有限。
- 配置需显式声明:项目必须在
moon.yml中注册,不如 Nx 自动发现灵活。
4.5 Nx 测试流程
步骤 1:初始化 Nx 项目
# 在干净目录中初始化
npx create-nx-workspace@latest monorepo-nx-test --preset=react-monorepo --appName=web --style=css --nxCloud=false
cd monorepo-nx-test
Nx 默认集成 pnpm(或 yarn/npm,取决于选择),并自动生成
apps/web和基础配置。
使用--nxCloud=false可关闭远程缓存以避免付费提示。
步骤 2:创建共享库并注册项目
Nx 支持通过代码生成器自动创建库,无需手动注册:
# 自动生成 libs/utils 库(TypeScript + Jest)
nx g @nx/react:lib utils --buildable --importPath=@myorg/utils
✅ 此命令会:
- 创建
libs/utils目录;- 配置
tsconfig路径别名;- 在
project.json中注册项目;- 自动设置构建目标(使用
tsc或rollup,取决于是否--buildable)。
步骤 3:配置依赖关系
将 utils 作为 web 的依赖:
nx g @nx/react:storybook-configuration web # (可选)仅为演示依赖注入
nx g add-dependency --project=web --dependency=@myorg/utils
或手动编辑 apps/web/package.json(Nx 实际通过 project.json 管理依赖图,但推荐使用 workspace 协议):
{
"name": "web",
"dependencies": {
"@myorg/utils": "workspace:*"
}
}
🔍 Nx 不依赖 package.json 的 dependencies 推断依赖,而是通过源码中的
import语句静态分析,因此即使未声明也能识别。但显式声明有助于包管理器(如 pnpm)正确链接。
步骤 4:执行命令测试
| 功能 | 命令 | 结果 |
|---|---|---|
| 构建 web | nx build web | 成功,自动检测并构建 utils(因 web 中 import 了它) |
| 增量缓存 | 再次运行相同命令 | 输出 With caching enabled... [existing outputs match hash] → skip,跳过执行 |
| 查看依赖图 | nx graph | 启动本地服务,打开交互式依赖图网页,清晰展示 web → utils 关系 |
✅ Nx 测试总结
- 自动化程度高:项目创建、路径别名、构建配置均由生成器完成;
- 依赖感知智能:基于源码分析,而非仅依赖
package.json; - 缓存机制成熟:本地缓存默认开启,支持远程缓存(Nx Cloud);
- 可视化强大:
nx graph是目前 Monorepo 工具中最友好的依赖图工具之一。
💡 对比 Moon:Nx 在 开发体验(代码生成、自动配置)上更胜一筹;Moon 则在 多语言支持 和 执行性能 上更具前瞻性。
4.6 综合对比表
| 能力 | Nx | Turborepo | Lerna | Rush | Moon |
|---|---|---|---|---|---|
| 增量构建 | ✅ 智能依赖感知 | ✅(需正确配置 outputs) | ⚠️ 仅 git diff | ✅ 基于变更集 | ✅ 自动依赖感知 |
| 本地缓存 | ✅ | ✅ | ❌ | ✅ | ✅ |
| 远程缓存 | ✅(Nx Cloud 付费) | ✅(Vercel 免费) | ❌ | ❌(需自建) | ✅(可自建,无厂商锁定) |
| 依赖图可视化 | ✅ 内置交互式 | ❌ | ❌ | ✅(SVG) | ✅(HTML/PNG) |
| 代码生成器 | ✅ 丰富插件 | ❌ | ❌ | ❌ | ❌ |
| 多技术栈支持 | ✅(Go/Java/Python 等) | ❌(仅 JS/TS) | ⚠️(仅 npm 脚本) | ⚠️(主攻 TS) | ✅(原生多语言) |
| 配置复杂度 | 中 | 低 | 低 | 高 | 中低 |
| CI/CD 优化 | ✅(需配置) | ✅(基础) | ❌ | ✅(企业级) | ✅(moon ci 内置智能) |
| 适合场景 | 复杂企业 Monorepo | React/Next.js 快速开发 | 纯 npm 包发布 | 超大型 TS 项目 | 高性能 + 多语言 Monorepo |
5.🎯 最终建议
若追求 功能完整性 + 成熟生态 + 代码生成 → Nx 仍是首选。若团队 专注 React/Next.js + 追求极致构建速度 + 免费远程缓存 → Turborepo 值得试点。若项目包含 Rust/Python/Go 等多语言,且希望统一管理、避免 SaaS 依赖 → Moon 是极具潜力的新选择。Lerna 仅推荐用于纯 npm 包发布场景。Rush 适合超大型 TypeScript 项目(如微软内部),一般团队慎选。
🔮 Moon 代表了下一代 Monorepo 工具的方向:高性能、多语言、去中心化缓存。虽生态尚早,但值得技术前瞻性团队评估。