生产级 turbo.json 范本
{
"$schema": "https://turbo.build/schema.json",
"ui": "tui",
"globalDependencies": [
".env*",
"tsconfig.base.json",
"tailwind.config.*",
"postcss.config.*"
],
"globalEnv": ["NODE_ENV", "VERCEL_ENV", "CI"],
"globalPassThroughEnv": [
"TURBO_*",
"GITHUB_TOKEN",
"NPM_TOKEN",
"AWS_*",
"SENTRY_AUTH_TOKEN"
],
"remoteCache": { "signature": true },
"tasks": {
"build": {
"dependsOn": ["^build"],
"inputs": [
"$TURBO_DEFAULT$",
"!**/*.md",
"!**/*.test.*",
"!**/__snapshots__/**"
],
"outputs": [
"dist/**",
".next/**",
"!.next/cache/**",
"storybook-static/**"
],
"env": ["NEXT_PUBLIC_*", "SENTRY_DSN", "DATABASE_URL"]
},
"test": {
"dependsOn": ["^build"],
"inputs": ["src/**", "test/**", "vitest.config.*"],
"outputs": ["coverage/**"]
},
"typecheck": {
"dependsOn": ["^build"],
"inputs": ["src/**", "tsconfig.json"]
},
"lint": {
"inputs": ["src/**", ".eslintrc*", "eslint.config.*"]
},
"dev": {
"cache": false,
"persistent": true
},
"//#generate-schema": {
"inputs": ["openapi.yaml"],
"outputs": ["packages/api-types/src/**"]
}
}
}
这份配置包含了几乎所有生产要素:全局依赖、签名校验、通配 env、精细 inputs/outputs、任务拓扑。
从 Turbo 1.x 迁移到 2.x
pnpm dlx @turbo/codemod migrate
常见变化:
pipeline → tasks
顶层键名改了,codemod 自动处理。
env 不再隐式
v1 下 env 自动被读取;v2 必须显式声明,否则 strict 模式读不到。codemod 扫代码里的 process.env 访问帮你列全。
dotenv 字段移除
v1 的
dotenv: [".env"] 字段废弃,改用 globalDependencies: [".env"] + 你自己的构建工具加载。--filter 语法收紧
部分旧语法(如
--filter=...{path})换成更清晰的。官方文档有完整 mapping。Rust 引擎
v1 是 Go 写的,v2 Rust;二进制更小,启动 50ms 以内,大仓任务调度更快。
boundaries:package 边界检查
pnpm turbo boundaries
Turbo 2.x 新功能,检查:
- packages/ui 是否 import 了没在 dependencies 声明的包?(hidden dependency)
- 跨包的相对路径 import(
import { x } from '../../other-pkg/src/y')——绕过了 package.json,危险 - 循环依赖(A 依赖 B,B 依赖 A)
// turbo.json 启用 { "boundaries": { "tags": { "@myorg/ui": ["ui", "shared"], "@myorg/web": ["app"] }, "rules": { "app": { "dependencies": { "allow": ["ui", "shared"] } }, "ui": { "dependencies": { "deny": ["app"] } } } } }
类似 Nx 的 enforce-module-boundaries——大仓的"包依赖纪律"工具。
常见生产问题
"缓存明明该命中,却一直 miss"
- 改了
globalDependencies里的文件(.env)—— 所有任务 hash 变 - CI 有时间戳进了产物—— 跑一次
--dry-run=json对比 hash - pnpm lockfile 换了—— 依赖 hash 连带变
- Turbo 版本升级—— hash 算法可能变,首次全 miss 是预期
"远程缓存上传失败但没报错"
- TURBO_TOKEN 过期—— 控制台重新生成
- 网络被墙(某些区域访问 Vercel cache 慢)—— 自建 Cloudflare R2 镜像
- 任务 outputs 没列全—— 上传的 tar 小,下载回来部分文件缺失
"生产构建和 CI 构建不一致"
- env 差异 —— Vercel 的 env 和 GH Actions 不同,产物自然不同
- Node 版本差异 —— 强制用
packageManager+engines锁 - Docker base image tag 飘移 —— 用 SHA 固定 base image
Turbo + Bun
bun install bun run turbo run build
Bun 1.2 官方支持 Turbo。workspaces 兼容 pnpm 布局,包管理器换成 Bun 也能无痛接 Turbo。只是:
- Bun 的 lockfile 是
bun.lockb,Turbo 已支持 - pnpm 的 Catalog 在 Bun 里支持略晚
- 远程缓存协议一样,TURBO_TOKEN 继续用
Turbo + Nx 对比的演进
| 功能 | Turbo 2.x | Nx 19.x |
|---|---|---|
| 远程缓存 | Vercel / 自建 | Nx Cloud / 自建 |
| 代码生成 | 无 | 强大 |
| 边界检查 | boundaries(2.x) | enforce-module-boundaries |
| 任务分发 | 有限(本地并发) | Nx Agents 多机分发 |
| 集成度 | 轻 | 深 |
| 上手 | 半小时 | 1-2 天 |
差距在缩小——Turbo 每次大版本补齐一个 Nx 特性(v2 补了 boundaries、watch、TUI)。对多数团队,"轻 + 够用"还是 Turbo 的核心价值。
性能调优实战
1. 量化当前水平
记录三个数:冷 CI、热 CI、命中率。
--summarize 可导出。2. 攻最大的任务
用
--profile=profile.json 看瓶颈,Next.js build / Storybook build 往往是最大头。3. 收窄 inputs
默认
$TURBO_DEFAULT$ 太宽。列具体目录后,命中率通常能从 60% 提到 90%+。4. 远程缓存必开
CI 和本地都受益,投入产出比极高。
5. 拆 job 并行
build/test/lint 各自 job——比单 job 串行快。
6. Vercel ignoreCommand
多 app 仓库,无关部署跳过——省部署额度、省等待时间。
大仓案例:100+ 包
一个真实的金融科技 monorepo(2026): - 12 个 apps(Next.js web + admin + mobile-rn + 2 个 API + worker + …) - 88 个 packages(ui / utils / api-types / db / auth / …) - pnpm + Turbo + Changesets + Vercel - 每天 80+ PR,一个 PR 平均 build 时间 45 秒(命中率 ~92%) - CI 月费比换 Turbo 前降了 70%
关键经验
- 每次加新包都 PR reviewer 检查 inputs/outputs 是否合理——防滑向 miss
- nightly job warm 远程缓存——开发者早晨 clone 零延迟
- Vercel 和 GH Actions 同 team,构建缓存完全共享
- 一个中心的 "monorepo health" dashboard 显示命中率、CI 时长趋势
什么时候"不用 Turbo"
- 单 app / 单包——没有任务图可优化
- 构建时间本来就 < 30 秒——加 Turbo 管理成本大于收益
- 构建严重依赖本地 state(比如随机 seed)——缓存没意义
- 非 JS/TS 项目为主——Turbo 对 Rust/Go/Python 支持很弱,还是 Bazel 更合适
Turborepo 2026 路线
- Task distribution:类似 Nx Agents,把 build 任务分发到多机并行(Vercel 试验中)
- 更智能的 inputs 推断:自动从 import graph 推出任务真正的输入文件
- Sandbox 执行:向 Bazel 靠拢,防止任务读了未声明的文件
- JSON Schema + IDE 自动完成:turbo.json 写起来更稳
- Turbo Pack 深度集成:共享 file watcher 和 hash 计算
写在最后
Turborepo 的价值不在"运行得快",在"让团队协作中的重复计算自动消失":
- 你已经 build 过的,同事不用再 build
- CI 已经跑过的,Vercel 不用再跑
- 一个 PR 只改一个包,其他 99 个包的测试不用跑
这是一个工程文化层面的变化——从"每次都全量"变成"只跑该跑的"。花两小时配对 turbo.json,能省团队几千个小时。
学习路径
- 先在已有 pnpm workspace 里
pnpm add -Dw turbo起手 - 写最小 turbo.json(build + test + dev 三个)
- 第二次 build 看到 FULL TURBO
- 接 Vercel Remote Cache,CI 时间立刻降
- 用
--summarize定期看命中率 - 收窄 inputs / outputs / env,把命中率推到 90%+
- 多 app 仓库上
turbo prune+turbo-ignore - Changesets + 远程缓存 + Vercel 集成,全自动发版
本章小结
- 生产 turbo.json 六要素:globalDeps / globalEnv / passThroughEnv / 任务 DAG / signature / TUI
- v1 → v2 codemod 自动迁移,主要变化:env 显式、pipeline 改 tasks、Rust 引擎
turbo boundaries是 2.x 的包依赖纪律工具- 性能调优顺序:量化 → 攻大头 → 收 inputs → 开远程缓存 → 拆 job
- Turbo 2026 向任务分发、sandbox、自动 inputs 推断演进——但核心哲学"简单够用"不会变