Turborepo 是一个面向 JavaScript 和 TypeScript monorepo 的高性能构建系统。由 Jared Palmer 创建,现由 Vercel 维护,Turborepo 通过智能缓存和任务调度大幅加速多包仓库的构建、测试和代码检查。无论你管理少量共享库还是数十个应用,Turborepo 通过理解包之间的依赖关系图来消除冗余工作,只重新构建发生变化的部分。
Turborepo 是一个 monorepo 构建系统,使用内容感知哈希、远程缓存(Vercel Remote Cache)和并行任务执行来将构建速度提升 65-85%。它支持 npm、pnpm 和 yarn 工作区,通过 turbo.json 进行最少配置,并集成任何 CI/CD 平台。Turborepo 理解任务依赖关系图,跳过已完成的工作,并在整个团队间共享缓存产物。
- Turborepo 使用内容感知哈希跳过输入未改变的任务,通常可减少 65-85% 的 CI 时间。
- 远程缓存在整个团队和 CI 间共享构建产物,一个开发者的构建结果惠及所有人。
- turbo.json 中的任务管道系统定义任务间的依赖关系,实现最大并行执行。
- Turborepo 支持 npm、pnpm 和 yarn 工作区,无需更换现有的包管理器。
- turbo prune 命令生成精简的 monorepo 子集,用于高效的 Docker 镜像构建。
- 相比 Nx 等替代方案,Turborepo 几乎零配置,使现有 monorepo 的采用变得简单。
什么是 Turborepo?为什么选择 Monorepo?
Monorepo 是一个包含多个项目、包或应用的单一仓库。Google、Meta 和 Microsoft 等公司多年来一直使用 monorepo,因为它简化了代码共享、统一了工具链,并使跨项目变更成为原子操作。然而,随着 monorepo 增长,构建时间会爆炸式增长。
Turborepo 作为一个智能任务运行器运行在包管理器工作区之上。它分析包之间的依赖图,确定哪些包受到更改影响,并以最优顺序、最大并行度执行任务。当任务以相同输入运行过时,Turborepo 在毫秒内重放缓存输出。
# Create a new Turborepo monorepo
npx create-turbo@latest my-monorepo
# Choose your package manager
? Which package manager do you want to use?
> pnpm (recommended)
npm
yarn
# Resulting structure:
my-monorepo/
apps/
web/ # Next.js application
docs/ # Documentation site
packages/
ui/ # Shared React component library
config-ts/ # Shared TypeScript configuration
config-eslint/ # Shared ESLint configuration
turbo.json # Turborepo configuration
package.json # Root workspace configuration
pnpm-workspace.yaml # pnpm workspace definitionTurborepo vs Nx vs Lerna 对比
Turborepo、Nx 和 Lerna 是 JavaScript 生态中最流行的三个 monorepo 工具,各有不同优势。
| 功能 | Turborepo | Nx | Lerna |
|---|---|---|---|
| 设置复杂度 | 极简(一个 turbo.json) | 中等(nx.json + project.json) | 极简(lerna.json) |
| 本地缓存 | 内置,内容感知 | 内置,计算缓存 | 通过 Nx 插件 |
| 远程缓存 | Vercel Remote Cache(免费层) | Nx Cloud(免费层) | 通过 Nx Cloud |
| 任务图 | turbo.json 管道 | project.json targets + nx.json | 基本拓扑排序 |
| 代码生成 | turbo gen(基础) | 丰富的生成器 + 插件 | 无内置功能 |
| Docker 裁剪 | turbo prune(内置) | 手动或社区插件 | 手动 |
| 插件生态 | 极简(专注工具) | 丰富(React、Angular、Node 等) | 极简 |
| 最适合 | 追求速度和最少配置的团队 | 需要全功能 monorepo 平台的企业团队 | 简单 monorepo 或遗留项目 |
turbo.json 配置
仓库根目录的 turbo.json 文件是 Turborepo 的唯一配置文件,定义任务管道、缓存行为和全局依赖。
// turbo.json
{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": [
"**/.env.*local",
".env"
],
"globalEnv": ["CI", "NODE_ENV"],
"tasks": {
"build": {
"dependsOn": ["^build"],
"inputs": ["src/**", "tsconfig.json", "package.json"],
"outputs": ["dist/**", ".next/**", "!.next/cache/**"],
"env": ["NEXT_PUBLIC_API_URL", "DATABASE_URL"]
},
"test": {
"dependsOn": ["build"],
"inputs": ["src/**", "test/**", "vitest.config.*"],
"outputs": ["coverage/**"],
"env": ["TEST_DATABASE_URL"]
},
"lint": {
"dependsOn": ["^build"],
"inputs": ["src/**", ".eslintrc.*", "eslint.config.*"]
},
"typecheck": {
"dependsOn": ["^build"],
"inputs": ["src/**", "tsconfig.json"]
},
"dev": {
"cache": false,
"persistent": true
},
"clean": {
"cache": false
}
}
}管道与任务图
Turborepo 管道定义 monorepo 中任务间的关系。运行 turbo run build 时,Turborepo 读取管道配置理解任务依赖,然后以最优顺序、最大并行度执行。
关键是 dependsOn 字段。当任务列出 "^build" 作为依赖时,^ 表示依赖包依赖项的 build 任务,而非同一包中的 build 任务。这确保共享库在消费它们的应用之前构建。
# Run build for all packages (respects dependency graph)
turbo run build
# Run multiple tasks in the correct order
turbo run build test lint
# Run build only for the web app and its dependencies
turbo run build --filter=web
# Run build for packages affected by changes since main
turbo run build --filter=...[main...HEAD]
# Run tests only for packages that changed
turbo run test --filter=[HEAD^1]
# Visualize the task graph (opens in browser)
turbo run build --graph
# Dry run: show what would execute without running
turbo run build --dry=json使用 Vercel Remote Cache 的远程缓存
远程缓存是 Turborepo 最具影响力的功能。启用后,构建产物在整个团队和 CI 基础设施间共享。开发者 A 本地构建的包,开发者 B 和 CI 管道可以直接复用。
Vercel 为 Turborepo 提供免费的远程缓存层。你也可以使用开源实现自托管远程缓存服务器。
# Step 1: Login to Vercel
turbo login
# Step 2: Link your repo to a Vercel project
turbo link
# Now all turbo commands use remote caching automatically
turbo run build
# >>> FULL TURBO (cache hit from remote)
# For CI: use a Vercel token instead of interactive login
# Set TURBO_TOKEN and TURBO_TEAM environment variables
TURBO_TOKEN=your_vercel_token \
TURBO_TEAM=your_team_slug \
turbo run build
# Disable remote caching temporarily
turbo run build --remote-only=false
# View cache status for a run
turbo run build --summarize工作区设置:npm、pnpm 和 yarn
Turborepo 运行在包管理器工作区之上,不替代 npm、pnpm 或 yarn,而是用缓存和任务编排增强它们。
pnpm (Recommended)
# pnpm-workspace.yaml
packages:
- "apps/*"
- "packages/*"
# Root package.json
{
"name": "my-monorepo",
"private": true,
"packageManager": "pnpm@9.15.0",
"scripts": {
"build": "turbo run build",
"dev": "turbo run dev",
"lint": "turbo run lint",
"test": "turbo run test"
},
"devDependencies": {
"turbo": "^2.4.0"
}
}npm Workspaces
// Root package.json for npm workspaces
{
"name": "my-monorepo",
"private": true,
"workspaces": [
"apps/*",
"packages/*"
],
"scripts": {
"build": "turbo run build",
"dev": "turbo run dev"
},
"devDependencies": {
"turbo": "^2.4.0"
}
}共享包与内部包
内部包是组织良好的 monorepo 的基石,允许在应用间共享代码(UI 组件、工具函数、TypeScript 配置、ESLint 配置)而无需发布到 npm。
工作区协议(workspace:*)告诉包管理器从本地工作区解析依赖而非 npm 注册表,开发时共享包的更改立即反映在消费应用中。
// packages/ui/package.json
{
"name": "@repo/ui",
"version": "0.0.0",
"private": true,
"main": "./src/index.ts",
"types": "./src/index.ts",
"exports": {
".": "./src/index.ts",
"./button": "./src/button.tsx",
"./card": "./src/card.tsx"
},
"dependencies": {
"react": "^19.0.0"
},
"devDependencies": {
"@repo/config-typescript": "workspace:*",
"typescript": "^5.7.0"
}
}
// apps/web/package.json (consuming the shared package)
{
"name": "web",
"dependencies": {
"@repo/ui": "workspace:*",
"next": "^15.0.0",
"react": "^19.0.0"
}
}
// Usage in apps/web/src/app/page.tsx
import { Button } from "@repo/ui/button";
import { Card } from "@repo/ui/card";
export default function Home() {
return (
<Card>
<Button onClick={() => alert("clicked")}>
Get Started
</Button>
</Card>
);
}Monorepo 中的 TypeScript 配置
在 monorepo 中管理 TypeScript 需要分层配置策略。在内部包中创建共享基础 tsconfig,然后在每个应用和包中继承它。
// packages/config-typescript/base.json
{
"compilerOptions": {
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"isolatedModules": true,
"moduleDetection": "force",
"declaration": true,
"declarationMap": true,
"sourceMap": true
}
}
// packages/config-typescript/nextjs.json
{
"extends": "./base.json",
"compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"],
"module": "esnext",
"moduleResolution": "bundler",
"jsx": "preserve",
"noEmit": true,
"incremental": true,
"plugins": [{ "name": "next" }]
}
}
// apps/web/tsconfig.json
{
"extends": "@repo/config-typescript/nextjs.json",
"compilerOptions": {
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["next-env.d.ts", "src/**/*.ts", "src/**/*.tsx"],
"exclude": ["node_modules"]
}GitHub Actions CI/CD 集成
Turborepo 与 GitHub Actions 等 CI 平台无缝集成。关键优化是启用远程缓存,使 CI 运行受益于其他运行和本地开发产生的缓存产物。
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
TURBO_TOKEN: \${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: \${{ vars.TURBO_TEAM }}
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 2
- 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, and test
run: turbo run build lint test
- name: Build only affected packages (PRs)
if: github.event_name == 'pull_request'
run: turbo run build --filter=...[origin/main...HEAD]增量构建与内容哈希
Turborepo 通过计算任务输入的内容感知哈希来决定是否重新运行。输入包括包中的源文件、turbo.json 管道配置、env 中的环境变量以及依赖包的哈希值。
这种方法比基于时间戳的失效更可靠,因为它能捕获从 git 恢复或从其他机器复制文件的情况。
# View the hash inputs for a task
turbo run build --dry=json
# Output includes hash details:
# {
# "taskId": "web#build",
# "hash": "a1b2c3d4e5f6",
# "inputs": {
# "src/app/page.tsx": "sha256:...",
# "package.json": "sha256:...",
# "tsconfig.json": "sha256:..."
# },
# "hashOfExternalDependencies": "sha256:...",
# "environmentVariables": {
# "NEXT_PUBLIC_API_URL": "sha256:..."
# },
# "cache": {
# "status": "HIT",
# "source": "REMOTE"
# }
# }
# Force re-execution (ignore cache)
turbo run build --force
# Generate a summary of cache performance
turbo run build --summarizeDocker 构建裁剪
为 monorepo 应用构建 Docker 镜像很棘手,因为 Docker COPY 会复制整个仓库。turbo prune 命令生成仅包含特定应用所需文件的精简子集。
# Dockerfile for a monorepo app using turbo prune
FROM node:20-alpine AS base
RUN apk add --no-cache libc6-compat
RUN corepack enable && corepack prepare pnpm@9 --activate
# Stage 1: Prune the monorepo for the target app
FROM base AS pruner
WORKDIR /app
COPY . .
RUN npx turbo prune web --docker
# Stage 2: Install dependencies
FROM base AS installer
WORKDIR /app
COPY --from=pruner /app/out/json/ .
COPY --from=pruner /app/out/pnpm-lock.yaml ./pnpm-lock.yaml
RUN pnpm install --frozen-lockfile
# Stage 3: Build the application
COPY --from=pruner /app/out/full/ .
RUN pnpm turbo run build --filter=web
# Stage 4: Production image
FROM base AS runner
WORKDIR /app
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
USER nextjs
COPY --from=installer /app/apps/web/.next/standalone ./
COPY --from=installer /app/apps/web/.next/static ./apps/web/.next/static
COPY --from=installer /app/apps/web/public ./apps/web/public
CMD ["node", "apps/web/server.js"]生成器:脚手架新包
Turborepo 包含代码生成功能(turbo gen),帮助你在 monorepo 中创建新包和应用。生成器底层使用 Plop 模板。
# Generate a new package interactively
turbo gen workspace
# Create a custom generator
# turbo/generators/config.ts
import type { PlopTypes } from "@turbo/gen";
export default function generator(plop: PlopTypes.NodePlopAPI) {
plop.setGenerator("react-package", {
description: "Create a new React package",
prompts: [
{
type: "input",
name: "name",
message: "Package name (e.g., button):",
},
],
actions: [
{
type: "add",
path: "packages/{{ name }}/package.json",
templateFile: "templates/package.json.hbs",
},
{
type: "add",
path: "packages/{{ name }}/src/index.ts",
template: 'export * from "./{{ name }}";',
},
{
type: "add",
path: "packages/{{ name }}/tsconfig.json",
templateFile: "templates/tsconfig.json.hbs",
},
],
});
}
# Run the custom generator
turbo gen react-package环境变量处理
环境变量是缓存等式的关键部分。如果构建依赖于 API_URL 等环境变量,需要在 turbo.json 中声明以便变量更改时使缓存失效。
env 键是每个任务的,只影响该任务的哈希。globalEnv 键应用于管道中的所有任务。
// turbo.json - Environment variable configuration
{
"globalEnv": [
"CI",
"NODE_ENV",
"VERCEL_URL"
],
"globalDependencies": [
".env",
".env.local"
],
"tasks": {
"build": {
"dependsOn": ["^build"],
"env": [
"NEXT_PUBLIC_API_URL",
"NEXT_PUBLIC_GA_ID",
"DATABASE_URL"
]
},
"test": {
"env": [
"TEST_DATABASE_URL",
"MOCK_API"
]
}
}
}
# Use environment variable passthrough for wildcards
# turbo.json supports prefix matching:
# "env": ["NEXT_PUBLIC_*"] matches all NEXT_PUBLIC_ variables迁移指南:将 Turborepo 添加到现有 Monorepo
在现有 monorepo 中采用 Turborepo 很简单,因为它与现有的包管理器和脚本配合工作,无需重构仓库。
- 步骤 1:在 monorepo 根目录安装 Turborepo 作为开发依赖。
- 步骤 2:在仓库根目录创建 turbo.json 文件定义任务管道。
- 步骤 3:更新根 package.json 脚本使用 turbo run 替代包管理器工作区命令。
- 步骤 4:运行 turbo login 和 turbo link 启用远程缓存。
- 步骤 5:更新 CI 管道使用 turbo run 配合 --filter 标志。
# Step 1: Install Turborepo
pnpm add -Dw turbo
# Step 2: Create turbo.json (see configuration section above)
# Step 3: Update root package.json scripts
# Before:
# "build": "pnpm -r run build"
# "test": "pnpm -r run test"
# After:
# "build": "turbo run build"
# "test": "turbo run test"
# Step 4: Enable remote caching
turbo login
turbo link
# Step 5: Run your first cached build
turbo run build
# First run: executes everything normally
# Second run: >>> FULL TURBO (all cache hits)最佳实践
- 将 turbo.json 作为任务依赖的唯一真实来源,避免在 package.json 脚本中重复管道逻辑。
- 始终在 turbo.json 的 env 或 globalEnv 中声明影响构建输出的环境变量。
- 内部包依赖使用 workspace:* 协议确保开发时本地解析。
- 从第一天起启用远程缓存,即使是个人开发者也能受益于 CI 缓存产物。
- Docker 构建使用 turbo prune 最小化镜像大小并最大化层缓存。
- 将共享代码结构化为专注的内部包(ui、utils、config-typescript、config-eslint)。
- 开发时使用 --filter 为特定包运行任务,而非运行所有内容。
- 使用 package.json 中的 packageManager 或 .turbo/config.json 固定团队的 Turborepo 版本。
- 使用 turbo run dev 设置监视模式,利用持久任务和变更自动重启。
- 使用 --summarize 分析任务执行以识别瓶颈和优化管道依赖。
常见问题
Turborepo 用来做什么?
Turborepo 是面向 JavaScript 和 TypeScript monorepo 的构建系统和任务运行器。它通过缓存任务结果和仅重新执行输入已更改的任务来加速构建、测试和代码检查。
Turborepo 缓存如何工作?
Turborepo 基于源文件、环境变量和依赖哈希为每个任务计算内容感知哈希。如果哈希匹配之前的运行,Turborepo 重放缓存的输出文件和终端日志。
Turborepo 比 Nx 好吗?
Turborepo 是专注的构建系统,配置最少,擅长缓存和任务调度。Nx 是全功能 monorepo 平台,有丰富的代码生成器和框架插件。简单选 Turborepo,全面管理选 Nx。
Turborepo 支持 pnpm 吗?
支持。Turborepo 原生支持 pnpm 工作区,pnpm 因其严格的依赖隔离和高效的磁盘存储而成为推荐的包管理器。
什么是 Turborepo 远程缓存?
远程缓存将构建产物存储在共享缓存中(默认 Vercel Remote Cache),团队成员和 CI 可以复用缓存结果而非从头构建。通常减少 65-85% 的 CI 时间。
如何从零开始创建 Turborepo monorepo?
运行 npx create-turbo@latest 创建新的 Turborepo monorepo,包括 apps 目录、packages 目录、turbo.json 配置和工作区配置。也可以在现有 monorepo 中安装 turbo 并创建 turbo.json。
Turborepo 能配合 Docker 使用吗?
可以。turbo prune 命令专为 Docker 构建设计,生成仅包含特定应用所需文件和依赖的精简子集,产生更小的镜像和更好的层缓存。
Turborepo 免费吗?
Turborepo 是 MIT 许可的开源项目,免费使用。Vercel 提供免费的远程缓存层。大型团队可选择 Vercel 付费计划或使用开源实现自托管远程缓存。