Monorepo管理:Turborepo/Nx
约 1839 字大约 6 分钟
monorepoturborepo
2025-08-07
概述
Monorepo 是将多个项目(packages、apps、libraries)放在同一个代码仓库中管理的策略。相比 Polyrepo(每个项目一个仓库),Monorepo 提供了更好的代码共享、统一的工具链和原子化的跨项目提交。Turborepo 和 Nx 是目前最流行的 Monorepo 管理工具。
Monorepo vs Polyrepo
| 方面 | Monorepo | Polyrepo |
|---|---|---|
| 代码共享 | 直接引用,实时同步 | 需发版后安装 |
| 依赖管理 | 统一版本,去重 | 各自管理,可能冲突 |
| 原子提交 | 跨项目修改在一个 commit | 需多个 PR 协调 |
| CI/CD | 需要增量构建优化 | 天然隔离,各自构建 |
| 代码可见性 | 全团队可见所有代码 | 可设置独立权限 |
| 仓库大小 | 随时间增长 | 各仓库保持轻量 |
1. Workspace 协议
所有主流包管理器都支持 Workspace,实现 Monorepo 内包之间的本地链接。
pnpm Workspace(推荐)
# pnpm-workspace.yaml
packages:
- 'packages/*'
- 'apps/*'
- 'tools/*'// packages/ui/package.json
{
"name": "@myorg/ui",
"version": "1.0.0",
"main": "./dist/index.js",
"types": "./dist/index.d.ts"
}
// apps/web/package.json
{
"name": "@myorg/web",
"dependencies": {
"@myorg/ui": "workspace:*", // 引用本地包
"@myorg/utils": "workspace:^1.0.0"
}
}# pnpm 常用命令
pnpm install # 安装所有依赖
pnpm --filter @myorg/web add axios # 为特定包添加依赖
pnpm --filter @myorg/web dev # 运行特定包的脚本
pnpm -r run build # 所有包执行 build
pnpm --filter "./packages/**" run test # 过滤模式匹配npm Workspace
// 根 package.json
{
"workspaces": [
"packages/*",
"apps/*"
]
}yarn Workspace
// 根 package.json
{
"workspaces": {
"packages": ["packages/*", "apps/*"],
"nohoist": ["**/react-native"]
}
}2. Turborepo
Turborepo(Vercel)专注于任务编排和缓存,是最轻量的 Monorepo 工具。
配置
// turbo.json
{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": [".env"],
"tasks": {
"build": {
"dependsOn": ["^build"], // 先构建依赖的包
"outputs": ["dist/**", ".next/**"], // 缓存输出
"env": ["NODE_ENV"]
},
"test": {
"dependsOn": ["build"], // 测试前先构建
"inputs": ["src/**", "test/**"], // 缓存输入
"outputs": ["coverage/**"]
},
"lint": {
"dependsOn": [], // 无依赖,可并行
"outputs": []
},
"dev": {
"cache": false, // 开发模式不缓存
"persistent": true // 长期运行的任务
},
"typecheck": {
"dependsOn": ["^build"],
"outputs": []
}
}
}Task Pipeline
# Turborepo 命令
turbo run build # 构建所有包(利用依赖图并行)
turbo run build --filter=@myorg/web # 只构建 web 及其依赖
turbo run build --filter=@myorg/web... # web 及其所有下游
turbo run build --filter=...[HEAD~1] # 只构建有变更的包
turbo run lint test --parallel # 并行运行 lint 和 test
# 缓存命中时跳过执行
# >>> FULL TURBO: @myorg/utils#build cache hit, replaying output缓存机制
Remote Caching
# 启用 Vercel Remote Cache
turbo login
turbo link
# 或自建远程缓存
# turbo.json
{
"remoteCache": {
"enabled": true,
"signature": true
}
}
# CI 中使用远程缓存
# .github/workflows/ci.yml
- name: Build
run: turbo run build
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: my-team3. Nx
Nx(Nrwl)是功能更全面的 Monorepo 工具,提供项目图分析、代码生成器和丰富的插件生态。
配置
// nx.json
{
"targetDefaults": {
"build": {
"dependsOn": ["^build"],
"inputs": ["production"],
"cache": true
},
"test": {
"inputs": ["default", "^production"],
"cache": true
},
"lint": {
"inputs": ["default"],
"cache": true
}
},
"namedInputs": {
"default": ["{projectRoot}/**/*"],
"production": [
"default",
"!{projectRoot}/**/*.spec.ts",
"!{projectRoot}/tsconfig.spec.json"
]
},
"defaultBase": "main"
}Project Graph(项目依赖图)
# Nx 命令
npx nx build web-app # 构建指定项目
npx nx affected --target=build # 只构建受变更影响的项目
npx nx affected --target=test --base=main # 与 main 对比的受影响测试
npx nx graph # 可视化项目依赖图
npx nx run-many --target=build --all # 构建所有项目
npx nx run-many --target=lint --projects=ui,utils # 指定项目
# 代码生成器
npx nx generate @nx/react:component MyButton --project=ui
npx nx generate @nx/node:library shared-utilsAffected Commands
4. Lerna(经典方案)
Lerna 是最早的 Monorepo 工具,现由 Nx 团队维护。
// lerna.json
{
"version": "independent", // 各包独立版本 / "fixed" 统一版本
"npmClient": "pnpm",
"useWorkspaces": true,
"command": {
"publish": {
"conventionalCommits": true,
"message": "chore(release): publish"
}
}
}# Lerna 命令
lerna run build # 所有包运行 build
lerna run test --scope=@myorg/ui # 指定包运行 test
lerna publish # 发布变更的包
lerna version # 更新版本号
lerna changed # 查看哪些包有变更5. Changesets(版本管理)
Changesets 是 Monorepo 的版本管理和发布工具。
# 安装
pnpm add -Dw @changesets/cli
npx changeset init# .changeset/config.json
{
"changelog": "@changesets/cli/changelog",
"commit": false,
"fixed": [],
"linked": [["@myorg/ui", "@myorg/utils"]],
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch"
}# 创建 changeset(交互式选择变更的包和类型)
npx changeset
# 选择: @myorg/ui (minor), @myorg/utils (patch)
# 描述: Added new Button component
# 生成的文件: .changeset/funny-cats-dance.md
# ---
# "@myorg/ui": minor
# "@myorg/utils": patch
# ---
# Added new Button component
# 在 CI 中消费 changesets
npx changeset version # 根据 changesets 更新版本号和 CHANGELOG
npx changeset publish # 发布到 npm registry6. 完整的 Monorepo 项目结构
my-monorepo/
├── .changeset/
│ └── config.json
├── .github/
│ └── workflows/
│ ├── ci.yml
│ └── release.yml
├── apps/
│ ├── web/ # Next.js Web 应用
│ │ ├── src/
│ │ ├── package.json
│ │ └── tsconfig.json
│ ├── mobile/ # React Native 应用
│ │ └── ...
│ └── docs/ # 文档站点
│ └── ...
├── packages/
│ ├── ui/ # 共享 UI 组件库
│ │ ├── src/
│ │ ├── package.json
│ │ └── tsconfig.json
│ ├── utils/ # 共享工具库
│ │ └── ...
│ ├── config/ # 共享配置
│ │ ├── eslint/
│ │ ├── tsconfig/
│ │ └── tailwind/
│ └── types/ # 共享类型定义
│ └── ...
├── turbo.json # Turborepo 配置
├── pnpm-workspace.yaml # pnpm workspace
├── package.json # 根 package.json
├── tsconfig.base.json # 基础 TypeScript 配置
└── .eslintrc.js # 根 ESLint 配置根 package.json
{
"private": true,
"scripts": {
"build": "turbo run build",
"dev": "turbo run dev",
"lint": "turbo run lint",
"test": "turbo run test",
"typecheck": "turbo run typecheck",
"clean": "turbo run clean",
"format": "prettier --write \"**/*.{ts,tsx,md}\""
},
"devDependencies": {
"turbo": "^2.0.0",
"prettier": "^3.0.0",
"@changesets/cli": "^2.27.0"
},
"engines": {
"node": ">=18",
"pnpm": ">=9"
},
"packageManager": "pnpm@9.0.0"
}7. CI/CD 配置
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 2 # 用于 affected 分析
- uses: pnpm/action-setup@v4
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- name: Build, Lint, Test (with Turbo cache)
run: turbo run build lint test
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}Turborepo vs Nx 对比
| 特性 | Turborepo | Nx |
|---|---|---|
| 定位 | 任务编排 + 缓存 | 全功能 Monorepo 平台 |
| 学习曲线 | 低 | 中高 |
| 配置复杂度 | 简单 | 丰富 |
| 代码生成器 | 无 | 内置(强大) |
| 项目图分析 | 基于 package.json | 深度静态分析 |
| 插件生态 | 无 | 丰富(React, Angular, Node...) |
| 远程缓存 | Vercel / 自建 | Nx Cloud / 自建 |
| 适合场景 | 轻量级 Monorepo | 大型企业级 Monorepo |
总结
Monorepo 通过统一仓库管理多个项目,实现了代码共享、统一工具链和原子化提交。pnpm Workspace 提供了高效的依赖管理和本地包链接。Turborepo 以极简配置实现了强大的任务编排和缓存机制,适合大多数项目。Nx 提供更全面的功能(代码生成、深度依赖分析、丰富插件),适合大型企业项目。Changesets 解决了 Monorepo 中的版本管理和发布问题。选择工具时,应根据团队规模、项目复杂度和已有技术栈做出决策。
贡献者
更新日志
2026/3/14 13:09
查看所有更新日志
9f6c2-feat: organize wiki content and refresh site setup于