turborepo-caching
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseTurborepo Caching
Turborepo 缓存
Production patterns for Turborepo build optimization.
用于Turborepo构建优化的生产级实践方案。
When to Use This Skill
适用场景
- Setting up new Turborepo projects
- Configuring build pipelines
- Implementing remote caching
- Optimizing CI/CD performance
- Migrating from other monorepo tools
- Debugging cache misses
- 搭建新的Turborepo项目
- 配置构建流水线
- 实现远程缓存
- 优化CI/CD性能
- 从其他单体仓库工具迁移
- 调试缓存未命中问题
Core Concepts
核心概念
1. Turborepo Architecture
1. Turborepo 架构
Workspace Root/
├── apps/
│ ├── web/
│ │ └── package.json
│ └── docs/
│ └── package.json
├── packages/
│ ├── ui/
│ │ └── package.json
│ └── config/
│ └── package.json
├── turbo.json
└── package.jsonWorkspace Root/
├── apps/
│ ├── web/
│ │ └── package.json
│ └── docs/
│ └── package.json
├── packages/
│ ├── ui/
│ │ └── package.json
│ └── config/
│ └── package.json
├── turbo.json
└── package.json2. Pipeline Concepts
2. 流水线概念
| Concept | Description |
|---|---|
| dependsOn | Tasks that must complete first |
| cache | Whether to cache outputs |
| outputs | Files to cache |
| inputs | Files that affect cache key |
| persistent | Long-running tasks (dev servers) |
| Concept | 描述 |
|---|---|
| dependsOn | 必须先完成的任务 |
| cache | 是否缓存输出结果 |
| outputs | 需要缓存的文件 |
| inputs | 影响缓存键的文件 |
| persistent | 长期运行的任务(如开发服务器) |
Templates
模板
Template 1: turbo.json Configuration
模板1:turbo.json 配置
json
{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": [".env", ".env.local"],
"globalEnv": ["NODE_ENV", "VERCEL_URL"],
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**", "!.next/cache/**"],
"env": ["API_URL", "NEXT_PUBLIC_*"]
},
"test": {
"dependsOn": ["build"],
"outputs": ["coverage/**"],
"inputs": ["src/**/*.tsx", "src/**/*.ts", "test/**/*.ts"]
},
"lint": {
"outputs": [],
"cache": true
},
"typecheck": {
"dependsOn": ["^build"],
"outputs": []
},
"dev": {
"cache": false,
"persistent": true
},
"clean": {
"cache": false
}
}
}json
{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": [".env", ".env.local"],
"globalEnv": ["NODE_ENV", "VERCEL_URL"],
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**", "!.next/cache/**"],
"env": ["API_URL", "NEXT_PUBLIC_*"]
},
"test": {
"dependsOn": ["build"],
"outputs": ["coverage/**"],
"inputs": ["src/**/*.tsx", "src/**/*.ts", "test/**/*.ts"]
},
"lint": {
"outputs": [],
"cache": true
},
"typecheck": {
"dependsOn": ["^build"],
"outputs": []
},
"dev": {
"cache": false,
"persistent": true
},
"clean": {
"cache": false
}
}
}Template 2: Package-Specific Pipeline
模板2:包专属流水线配置
json
// apps/web/turbo.json
{
"$schema": "https://turbo.build/schema.json",
"extends": ["//"],
"pipeline": {
"build": {
"outputs": [".next/**", "!.next/cache/**"],
"env": ["NEXT_PUBLIC_API_URL", "NEXT_PUBLIC_ANALYTICS_ID"]
},
"test": {
"outputs": ["coverage/**"],
"inputs": ["src/**", "tests/**", "jest.config.js"]
}
}
}json
// apps/web/turbo.json
{
"$schema": "https://turbo.build/schema.json",
"extends": ["//"],
"pipeline": {
"build": {
"outputs": [".next/**", "!.next/cache/**"],
"env": ["NEXT_PUBLIC_API_URL", "NEXT_PUBLIC_ANALYTICS_ID"]
},
"test": {
"outputs": ["coverage/**"],
"inputs": ["src/**", "tests/**", "jest.config.js"]
}
}
}Template 3: Remote Caching with Vercel
模板3:结合Vercel实现远程缓存
bash
undefinedbash
undefinedLogin to Vercel
登录Vercel
npx turbo login
npx turbo login
Link to Vercel project
关联到Vercel项目
npx turbo link
npx turbo link
Run with remote cache
使用远程缓存运行构建
turbo build --remote-only
turbo build --remote-only
CI environment variables
CI环境变量
TURBO_TOKEN=your-token
TURBO_TEAM=your-team
```yamlTURBO_TOKEN=your-token
TURBO_TEAM=your-team
```yaml.github/workflows/ci.yml
.github/workflows/ci.yml
name: CI
on:
push:
branches: [main]
pull_request:
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Build
run: npx turbo build --filter='...[origin/main]'
- name: Test
run: npx turbo test --filter='...[origin/main]'undefinedname: CI
on:
push:
branches: [main]
pull_request:
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Build
run: npx turbo build --filter='...[origin/main]'
- name: Test
run: npx turbo test --filter='...[origin/main]'undefinedTemplate 4: Self-Hosted Remote Cache
模板4:自建远程缓存服务
typescript
// Custom remote cache server (Express)
import express from "express";
import { createReadStream, createWriteStream } from "fs";
import { mkdir } from "fs/promises";
import { join } from "path";
const app = express();
const CACHE_DIR = "./cache";
// Get artifact
app.get("/v8/artifacts/:hash", async (req, res) => {
const { hash } = req.params;
const team = req.query.teamId || "default";
const filePath = join(CACHE_DIR, team, hash);
try {
const stream = createReadStream(filePath);
stream.pipe(res);
} catch {
res.status(404).send("Not found");
}
});
// Put artifact
app.put("/v8/artifacts/:hash", async (req, res) => {
const { hash } = req.params;
const team = req.query.teamId || "default";
const dir = join(CACHE_DIR, team);
const filePath = join(dir, hash);
await mkdir(dir, { recursive: true });
const stream = createWriteStream(filePath);
req.pipe(stream);
stream.on("finish", () => {
res.json({
urls: [`${req.protocol}://${req.get("host")}/v8/artifacts/${hash}`],
});
});
});
// Check artifact exists
app.head("/v8/artifacts/:hash", async (req, res) => {
const { hash } = req.params;
const team = req.query.teamId || "default";
const filePath = join(CACHE_DIR, team, hash);
try {
await fs.access(filePath);
res.status(200).end();
} catch {
res.status(404).end();
}
});
app.listen(3000);json
// turbo.json for self-hosted cache
{
"remoteCache": {
"signature": false
}
}bash
undefinedtypescript
// 自定义远程缓存服务器(基于Express)
import express from "express";
import { createReadStream, createWriteStream } from "fs";
import { mkdir } from "fs/promises";
import { join } from "path";
const app = express();
const CACHE_DIR = "./cache";
// 获取构件
app.get("/v8/artifacts/:hash", async (req, res) => {
const { hash } = req.params;
const team = req.query.teamId || "default";
const filePath = join(CACHE_DIR, team, hash);
try {
const stream = createReadStream(filePath);
stream.pipe(res);
} catch {
res.status(404).send("Not found");
}
});
// 上传构件
app.put("/v8/artifacts/:hash", async (req, res) => {
const { hash } = req.params;
const team = req.query.teamId || "default";
const dir = join(CACHE_DIR, team);
const filePath = join(dir, hash);
await mkdir(dir, { recursive: true });
const stream = createWriteStream(filePath);
req.pipe(stream);
stream.on("finish", () => {
res.json({
urls: [`${req.protocol}://${req.get("host")}/v8/artifacts/${hash}`],
});
});
});
// 检查构件是否存在
app.head("/v8/artifacts/:hash", async (req, res) => {
const { hash } = req.params;
const team = req.query.teamId || "default";
const filePath = join(CACHE_DIR, team, hash);
try {
await fs.access(filePath);
res.status(200).end();
} catch {
res.status(404).end();
}
});
app.listen(3000);json
// 适配自建缓存的turbo.json配置
{
"remoteCache": {
"signature": false
}
}bash
undefinedUse self-hosted cache
使用自建缓存服务运行构建
turbo build --api="http://localhost:3000" --token="my-token" --team="my-team"
undefinedturbo build --api="http://localhost:3000" --token="my-token" --team="my-team"
undefinedTemplate 5: Filtering and Scoping
模板5:过滤与范围指定
bash
undefinedbash
undefinedBuild specific package
构建指定包
turbo build --filter=@myorg/web
turbo build --filter=@myorg/web
Build package and its dependencies
构建指定包及其依赖
turbo build --filter=@myorg/web...
turbo build --filter=@myorg/web...
Build package and its dependents
构建指定包的所有依赖项
turbo build --filter=...@myorg/ui
turbo build --filter=...@myorg/ui
Build changed packages since main
构建自main分支以来有变更的包
turbo build --filter='...[origin/main]'
turbo build --filter='...[origin/main]'
Build packages in directory
构建指定目录下的包
turbo build --filter='./apps/*'
turbo build --filter='./apps/*'
Combine filters
组合过滤条件
turbo build --filter=@myorg/web --filter=@myorg/docs
turbo build --filter=@myorg/web --filter=@myorg/docs
Exclude package
排除指定包
turbo build --filter='!@myorg/docs'
turbo build --filter='!@myorg/docs'
Include dependencies of changed
包含变更包的依赖项
turbo build --filter='...[HEAD^1]...'
undefinedturbo build --filter='...[HEAD^1]...'
undefinedTemplate 6: Advanced Pipeline Configuration
模板6:高级流水线配置
json
{
"$schema": "https://turbo.build/schema.json",
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"],
"inputs": ["$TURBO_DEFAULT$", "!**/*.md", "!**/*.test.*"]
},
"test": {
"dependsOn": ["^build"],
"outputs": ["coverage/**"],
"inputs": ["src/**", "tests/**", "*.config.*"],
"env": ["CI", "NODE_ENV"]
},
"test:e2e": {
"dependsOn": ["build"],
"outputs": [],
"cache": false
},
"deploy": {
"dependsOn": ["build", "test", "lint"],
"outputs": [],
"cache": false
},
"db:generate": {
"cache": false
},
"db:push": {
"cache": false,
"dependsOn": ["db:generate"]
},
"@myorg/web#build": {
"dependsOn": ["^build", "@myorg/db#db:generate"],
"outputs": [".next/**"],
"env": ["NEXT_PUBLIC_*"]
}
}
}json
{
"$schema": "https://turbo.build/schema.json",
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"],
"inputs": ["$TURBO_DEFAULT$", "!**/*.md", "!**/*.test.*"]
},
"test": {
"dependsOn": ["^build"],
"outputs": ["coverage/**"],
"inputs": ["src/**", "tests/**", "*.config.*"],
"env": ["CI", "NODE_ENV"]
},
"test:e2e": {
"dependsOn": ["build"],
"outputs": [],
"cache": false
},
"deploy": {
"dependsOn": ["build", "test", "lint"],
"outputs": [],
"cache": false
},
"db:generate": {
"cache": false
},
"db:push": {
"cache": false,
"dependsOn": ["db:generate"]
},
"@myorg/web#build": {
"dependsOn": ["^build", "@myorg/db#db:generate"],
"outputs": [".next/**"],
"env": ["NEXT_PUBLIC_*"]
}
}
}Template 7: Root package.json Setup
模板7:根目录package.json配置
json
{
"name": "my-turborepo",
"private": true,
"workspaces": ["apps/*", "packages/*"],
"scripts": {
"build": "turbo build",
"dev": "turbo dev",
"lint": "turbo lint",
"test": "turbo test",
"clean": "turbo clean && rm -rf node_modules",
"format": "prettier --write \"**/*.{ts,tsx,md}\"",
"changeset": "changeset",
"version-packages": "changeset version",
"release": "turbo build --filter=./packages/* && changeset publish"
},
"devDependencies": {
"turbo": "^1.10.0",
"prettier": "^3.0.0",
"@changesets/cli": "^2.26.0"
},
"packageManager": "npm@10.0.0"
}json
{
"name": "my-turborepo",
"private": true,
"workspaces": ["apps/*", "packages/*"],
"scripts": {
"build": "turbo build",
"dev": "turbo dev",
"lint": "turbo lint",
"test": "turbo test",
"clean": "turbo clean && rm -rf node_modules",
"format": "prettier --write \\"**/*.{ts,tsx,md}\\"",
"changeset": "changeset",
"version-packages": "changeset version",
"release": "turbo build --filter=./packages/* && changeset publish"
},
"devDependencies": {
"turbo": "^1.10.0",
"prettier": "^3.0.0",
"@changesets/cli": "^2.26.0"
},
"packageManager": "npm@10.0.0"
}Debugging Cache
缓存调试
bash
undefinedbash
undefinedDry run to see what would run
空运行,查看会执行哪些任务
turbo build --dry-run
turbo build --dry-run
Verbose output with hashes
输出详细日志与哈希值
turbo build --verbosity=2
turbo build --verbosity=2
Show task graph
展示任务依赖图
turbo build --graph
turbo build --graph
Force no cache
强制不使用缓存运行构建
turbo build --force
turbo build --force
Show cache status
展示缓存状态
turbo build --summarize
turbo build --summarize
Debug specific task
调试指定任务
TURBO_LOG_VERBOSITY=debug turbo build --filter=@myorg/web
undefinedTURBO_LOG_VERBOSITY=debug turbo build --filter=@myorg/web
undefinedBest Practices
最佳实践
Do's
建议
- Define explicit inputs - Avoid cache invalidation
- Use workspace protocol -
"@myorg/ui": "workspace:*" - Enable remote caching - Share across CI and local
- Filter in CI - Build only affected packages
- Cache build outputs - Not source files
- 明确定义输入文件 - 避免不必要的缓存失效
- 使用工作区协议 - 如
"@myorg/ui": "workspace:*" - 启用远程缓存 - 在CI和本地环境间共享缓存
- 在CI中使用过滤 - 仅构建有变更的包
- 缓存构建输出 - 而非源代码
Don'ts
禁忌
- Don't cache dev servers - Use
persistent: true - Don't include secrets in env - Use runtime env vars
- Don't ignore dependsOn - Causes race conditions
- Don't over-filter - May miss dependencies
- 不要缓存开发服务器 - 使用 替代
persistent: true - 不要在环境变量中包含敏感信息 - 使用运行时环境变量
- 不要忽略dependsOn - 会导致竞争条件
- 不要过度过滤 - 可能会遗漏依赖项