Turborepo 单体仓库任务编排
bash mkdir my-monorepo && cd my-monorepo pnpm init
创建 `pnpm-workspace.yaml`,定义工作区包位置:
```yaml
packages:
- "apps/*"
- "packages/*"
手动创建目录结构:
my-monorepo/
├── apps/
│ ├── web/
│ └── docs/
├── packages/
│ └── ui/
├── pnpm-workspace.yaml
├── package.json
└── turbo.json
每个子包内部都有各自的 package.json 和代码,此处不展开。关键是要让它们包含常见的 scripts,如 build、test、lint。
2. 安装 Turborepo
全局安装或作为 devDependency 添加:
pnpm add -D turbo
根目录 package.json 添加便捷脚本:
{
"scripts": {
"build": "turbo run build",
"test": "turbo run test",
"lint": "turbo run lint",
"dev": "turbo run dev --parallel"
}
}
这样你就可以通过根目录调用 pnpm build 等命令来运行 Turborepo 编排的任务。
核心概念:任务管道 (Pipeline)
Turborepo 的行为由根目录的 turbo.json 配置文件控制。核心就是 pipeline 字段,它声明了仓库中每个任务应该如何被运行、依赖哪些任务。
最小配置示例
{
"$schema": "https://turbo.build/schema.json",
"pipeline": {
"build": {
"outputs": ["dist/**", ".next/**"]
},
"test": {},
"lint": {}
}
}
该配置告诉 Turborepo:
- 所有包的
build脚本被纳入编排,其产出目录为dist或.next(用于缓存)。 test和lint没有产出,但也会被编排。
声明依赖关系
真实场景中,任务之间往往存在先后依赖。例如 test 和 lint 应在 build 之前运行;如果一个包依赖另一个包的构建产物,则对应 build 任务需要按依赖拓扑顺序执行。这些都可以通过 dependsOn 声明:
{
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
},
"test": {
"dependsOn": ["build"]
},
"lint": {},
"dev": {
"cache": false
}
}
}
dependsOn: ["^build"]:表示一个包的build任务必须等待它所依赖(在 package.json 中声明)的所有包的build任务完成。这保证了构建顺序的正确性。dependsOn: ["build"]:表示当前任务需要等待同包内的build任务完成。例如,test必须在build之后运行。cache: false:对于dev这类服务器实时监听的任务,我们通常禁用缓存。
缓存机制:跳过重复工作
Turborepo 会根据输入文件的内容哈希和任务配置来判断是否可以复用缓存。当你运行 turbo run build 时,如果源代码未改变,Turborepo 将直接显示 "FULL TURBO" 并跳过执行,输出可从缓存恢复。
配置输入和输出
为了准确计算哈希,需要在 pipeline 中指定任务的 inputs(默认是所有工作区文件)和 outputs。建议显式声明,提高缓存命中率:
{
"pipeline": {
"build": {
"inputs": ["src/**/*.ts", "src/**/*.tsx", "tsconfig.json"],
"outputs": ["dist/**"]
}
}
}
Turborepo 还会自动包含包内的 package.json 和配置文件中声明的全局依赖文件(如根目录 turbo.json 本身)。
远程缓存
本地缓存仅对当前机器有效。使用 Turborepo 的远程缓存功能(如 Vercel 提供的免费远程缓存服务或自建服务),团队成员和 CI 环境可以共享缓存。只需在项目根目录设置环境变量并连接:
npx turbo login # 登录 Vercel 账号
npx turbo link # 链接到远程缓存
# 或手动设置自建缓存服务器 URL
TURBO_TOKEN=your_token TURBO_API=http://your-cache-server turbo run build
配置好后,CI 中的首次构建仍会执行完整编译,但后续任何人在任何分支上的相同构建都可能命中远程缓存,极大加速迭代。
并行执行与依赖拓扑
Turborepo 自动根据包的依赖树和 dependsOn 规则生成有向无环图(DAG),并以最大并行度执行任务。你无需手动管理 --parallel 或 --concurrency。默认最大并发数由 CPU 核心数决定,你也可以通过 --concurrency=10 调整。
turbo run build --concurrency=8
对于不冲突的任务,例如所有包的 lint,它们之间通常没有交叉依赖,会完全并行运行。这正是 turbo 比 nx 原生 nx.json 或 lerna 更简洁高效的地方。
环境变量与全局依赖
某些任务可能依赖于环境变量(如 NODE_ENV)。默认情况下,Turborepo 不会将环境变量纳入哈希计算,这可能导致缓存错误。你可以在 turbo.json 全局或任务级别声明 globalDependencies 和 globalEnv:
{
"globalEnv": ["NODE_ENV", "API_URL"],
"pipeline": {
"build": {
"env": ["DATABASE_URL"],
"outputs": ["dist/**"]
}
}
}
globalEnv:对所有任务生效。env:仅对当前任务生效。
请注意,若添加了新的环境变量,turbo 会自动将其纳入哈希;你也可以通过 globalDependencies 声明文件路径(如 .env)来监视环境文件的变化。
高级用法:从现有 Monorepo 迁移
如果你的仓库已经使用 Yarn/NPM/Lerna 等工具管理,集成 Turborepo 非常简单:
- 安装
turbo开发依赖。 - 从最简单的
turbo.json开始:
{
"pipeline": {
"build": {},
"test": {},
"lint": {}
}
}