
Monorepo 与 Polyrepo:如何做出正确选择
Monorepo 将多个项目存储在一个仓库中。Google、Facebook 和 Airbnb 等公司都使用 monorepo 来管理代码库。但这并不意味着它们适合每个团队。
选择 monorepo 的场景:
- 有多个应用共享的库
- 团队经常进行跨项目变更
- 希望统一工具、lint 和测试
- 正在构建设计系统或组件库
坚持使用 polyrepo 的场景:
- 项目使用完全不同的技术栈
- 团队完全独立工作
- 安全/合规要求严格分离

pnpm Workspaces:基础
大多数现代 monorepo 工具都基于包管理器工作区构建:
# pnpm-workspace.yaml(monorepo 根目录)
packages:
- 'apps/*' # app-web, app-mobile, app-api
- 'packages/*' # ui, utils, config, types
// 根 package.json
{
"name": "my-monorepo",
"private": true,
"scripts": {
"dev": "turbo dev",
"build": "turbo build",
"lint": "turbo lint",
"test": "turbo test"
},
"devDependencies": {
"turbo": "^2.0.0",
"@types/node": "^20.0.0",
"typescript": "^5.0.0"
}
}
# 为所有工作区安装依赖
pnpm install
# 向特定包添加依赖
pnpm add react --filter @myapp/web
# 添加共享内部包
pnpm add @myapp/ui --filter @myapp/web
Turborepo:快速缓存构建
Turborepo 是最简单、最流行的 monorepo 构建系统:
// turbo.json(根目录)
{
"$schema": "https://turbo.build/schema.json",
"tasks": {
"build": {
"dependsOn": ["^build"], // 先构建依赖
"outputs": ["dist/**", ".next/**"]
},
"test": {
"dependsOn": ["^build"],
"outputs": ["coverage/**"]
},
"lint": {
"outputs": []
},
"dev": {
"cache": false, // 从不缓存开发服务器
"persistent": true // 长时间运行的进程
}
}
}
# 为所有包运行构建(首次运行后缓存)
turbo build
# 仅对更改的包运行构建
turbo build --filter=[HEAD^1]
# 为特定应用及其依赖运行构建
turbo build --filter=@myapp/web...
# 清除缓存
turbo build --force

Turborepo 远程缓存
# 连接到 Vercel 远程缓存
npx turbo login
npx turbo link
# 现在 CI 机器共享同一缓存——未更改代码的构建时间为 0 秒!
# .github/workflows/ci.yml
- name: Build
run: turbo build
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
Nx:更强大,更复杂
Nx 更适合具有复杂依赖关系图的大型代码库:
# 创建新的 Nx monorepo
npx create-nx-workspace@latest myorg
# 生成应用和库
nx generate @nx/react:app web
nx generate @nx/node:app api
nx generate @nx/react:library ui
nx generate @nx/js:library utils
# 运行受影响的 task(仅自 main 分支以来更改的内容)
nx affected --target=build
nx affected --target=test --base=origin/main
# 依赖关系图可视化
nx graph
// nx.json
{
"targetDefaults": {
"build": {
"dependsOn": ["^build"],
"cache": true
},
"test": {
"cache": true,
"inputs": ["default", "^production"]
}
},
"namedInputs": {
"production": [
"default",
"!{projectRoot}/**/*.spec.ts",
"!{projectRoot}/jest.config.ts"
]
}
}
共享包结构
monorepo/
├── apps/
│ ├── web/ # Next.js 应用
│ │ └── package.json # 依赖 @myapp/ui, @myapp/utils
│ └── api/ # Express API
│ └── package.json # 依赖 @myapp/utils, @myapp/types
└── packages/
├── ui/ # 共享 React 组件
│ ├── src/
│ │ ├── Button.tsx
│ │ └── index.ts
│ ├── package.json
│ └── tsconfig.json
├── utils/ # 共享工具函数
├── types/ # 共享 TypeScript 类型
└── config/ # 共享 tsconfig, eslint 配置
// packages/ui/package.json
{
"name": "@myapp/ui",
"version": "0.0.0",
"main": "./src/index.ts", // 直接引用源码——开发时无需构建步骤
"types": "./src/index.ts",
"exports": {
".": {
"import": "./dist/index.js",
"types": "./dist/index.d.ts"
}
},
"scripts": {
"build": "tsc --build",
"dev": "tsc --watch"
},
"peerDependencies": {
"react": "^18.0.0"
}
}
// apps/web/package.json
{
"name": "@myapp/web",
"dependencies": {
"@myapp/ui": "workspace:*", // pnpm 工作区引用
"@myapp/utils": "workspace:*"
}
}
共享 TypeScript 配置
// packages/config/tsconfig.base.json
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "Bundler",
"strict": true,
"skipLibCheck": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true
}
}
// apps/web/tsconfig.json
{
"extends": "@myapp/config/tsconfig.base.json",
"compilerOptions": {
"lib": ["dom", "ES2022"],
"jsx": "react-jsx"
},
"include": ["src"]
}
共享 ESLint 配置
// packages/config/eslint.base.js
module.exports = {
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'],
rules: {
'@typescript-eslint/no-unused-vars': 'error',
'@typescript-eslint/explicit-function-return-type': 'off',
},
};
// apps/web/.eslintrc.js
const base = require('@myapp/config/eslint.base.js');
module.exports = {
...base,
extends: [...base.extends, 'plugin:react/recommended'],
};
Turborepo 与 Nx 对比
| 特性 | Turborepo | Nx |
|---|---|---|
| 搭建复杂度 | 简单 | 复杂 |
| 学习曲线 | 低 | 高 |
| 缓存 | ✅ 优秀 | ✅ 优秀 |
| 增量构建 | ✅ | ✅ |
| 代码生成 | ❌ 基础 | ✅ 丰富 |
| 插件生态 | 增长中 | 成熟 |
| 远程缓存 | Vercel(免费) | Nx Cloud(付费/自托管) |
| 最适合 | 中小型项目 | 大型企业 |
→ 使用 JSON 转 YAML 转换器 在 JSON 和 YAML 之间转换包配置。