一、开场引言
随着前端业务的不断扩张,我们往往会面临多项目维护、代码复用困难、依赖版本冲突等问题。Monorepo 正是解决这些痛点的一剂良药。本次分享将带大家从零认识 Monorepo,了解它的优缺点,并动手搭建一个最小可用的工程。
二、Monorepo 到底是什么?
Monorepo (Mono Repository)即单一代码仓库,指在一个代码仓库中管理多个独立的项目或模块。
🚫 常见误解澄清:
- 误解 1 :Monorepo = 把所有代码塞进一个巨大的文件里。
- 真相:它是多个独立项目的集合,目录结构清晰,各模块边界分明。
- 误解 2 :Monorepo 就是 Git Submodule。
- 真相 :Submodule 只是 Git 层面的嵌套,而 Monorepo 强调的是工作流、依赖管理和构建体系的统一。
三、Monorepo vs Polyrepo(多仓库)
| 维度 | Polyrepo (多仓库) | Monorepo (单仓库) |
|---|---|---|
| 代码复用 | 需发版到 npm,流程繁琐 | 直接通过 workspace:* 本地引用 |
| 依赖管理 | 各仓库独立,易出现版本碎片化 | 统一依赖树,避免版本冲突 |
| 重构与修改 | 需跨多个仓库提 PR,难以保证原子性 | 一次提交修改多个模块,保证一致性 |
| CI/CD 构建 | 独立构建,互不影响 | 需引入任务编排,避免全量构建 |
| 权限控制 | 仓库级别隔离,权限清晰 | 需借助工具或平台实现目录级权限 |
总结 :Monorepo 的收益在于极致的代码共享与一致性 ,代价在于对工具链和 CI/CD 要求较高。
四、主流工具生态一览
目前前端主流的 Monorepo 方案有以下几种:
- pnpm workspace :基于 pnpm 的硬链接/符号链接机制,极速且节省磁盘,目前最推荐的底层包管理器。
- Turborepo :专注于任务编排与增量构建,配置极简,与 pnpm 是黄金搭档。
- Nx:功能最强大的企业级方案,自带依赖图分析、代码生成器和分布式缓存,适合超大型团队。
- Lerna :曾经的王者,现主要聚焦于多包版本管理与发布,构建能力已逐渐被 Turborepo 取代。
- Rush:微软开源,适合极其庞大且复杂的工程,学习曲线较陡。
💡 入门选型建议 :pnpm workspace (包管理) + Turborepo (任务构建) + Changesets (版本发布)。
五、动手实践:用 pnpm workspace 搭最小骨架
1. 初始化与配置
bash
# 创建根目录并初始化
mkdir my-monorepo && cd my-monorepo
pnpm init
# 创建 pnpm-workspace.yaml
touch pnpm-workspace.yaml
在 pnpm-workspace.yaml 中声明工作区:
yaml
packages:
- 'packages/*'
- 'apps/*'
2. 创建子包
bash
mkdir -p packages/ui apps/web
cd packages/ui && pnpm init
cd ../../apps/web && pnpm init
3. 本地依赖引用
在 apps/web 中引用 ui 组件库:
bash
cd apps/web
pnpm add @my-org/ui@workspace:*
六、任务编排与增量构建 (Turborepo)
1. 安装与配置
bash
pnpm add turbo -Dw
在根目录创建 turbo.json:
json
{
"$schema": "https://turbo.build/schema.json",
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
},
"dev": {
"cache": false,
"persistent": true
}
}
}
2. 运行任务
bash
# 拓扑排序构建所有包
pnpm turbo build
# 启动所有应用的开发模式
pnpm turbo dev
Turborepo 会自动分析依赖图,先构建底层包,再构建上层应用,并自动利用缓存跳过未修改的包。
七、依赖管理与版本发布
推荐使用 Changesets 进行版本管理:
- 每次修改代码后,运行
pnpm changeset记录变更日志。 - 运行
pnpm changeset version自动 bump 版本号并生成 CHANGELOG。 - 运行
pnpm changeset publish统一发布到 npm。
八、常见问题与最佳实践
- 幽灵依赖:pnpm 严格的依赖隔离机制可以天然避免幽灵依赖,但需确保所有依赖都显式声明。
- 构建缓存失效 :确保
turbo.json中的outputs配置正确,避免缓存了错误的产物。 - ESLint / TSConfig 共享 :在
packages下建立eslint-config和tsconfig包,各应用通过extends继承,保持规范统一。 - 不要过早 Monorepo:如果团队只有 1-2 个独立项目,且没有强烈的代码复用需求,Polyrepo 依然是好选择。
九、总结
Monorepo 不是银弹,而是一种工程化架构选择。
- 核心收益:代码共享、统一规范、原子提交。
- 核心挑战:工具链搭建、CI/CD 优化。
- 入门路径 :从
pnpm workspace开始,逐步引入Turborepo和Changesets。