
TypeScript Monorepo with Turborepo
为什么选择 Turborepo?
大型 monorepo 的问题:
- 构建时间长(重新构建未更改的代码)
- 复杂的任务依赖(先构建 A 再构建 B)
- 无法共享构建缓存
Turborepo 的解决方案:
- 增量构建(仅重新构建更改的包)
- 远程缓存(在 CI 运行之间共享缓存)
- 具有依赖感知的并行执行
设置
# 创建新的 monorepo
npx create-turbo@latest my-monorepo
cd my-monorepo
# 或添加到现有项目
npm install -D turbo
结构
monorepo/
├── apps/
│ ├── web/ (Next.js)
│ ├── api/ (Express/Fastify)
│ └── mobile/ (React Native)
├── packages/
│ ├── ui/ (共享 React 组件)
│ ├── utils/ (共享工具函数)
│ ├── types/ (共享 TypeScript 类型)
│ └── config/
│ ├── eslint/
│ └── tsconfig/
├── turbo.json
└── package.json (根目录)
turbo.json 配置
{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": [".env.local"],
"pipeline": {
"build": {
"dependsOn": ["^build"], // 先构建依赖
"outputs": ["dist/**", ".next/**"],
"cache": true
},
"test": {
"dependsOn": ["^build"],
"outputs": ["coverage/**"],
"cache": true
},
"lint": {
"outputs": []
},
"dev": {
"cache": false,
"persistent": true // 长时间运行进程
},
"type-check": {
"outputs": []
}
}
}
共享包示例
// packages/ui/src/Button.tsx
import React from 'react';
export interface ButtonProps {
variant?: 'primary' | 'secondary';
children: React.ReactNode;
onClick?: () => void;
disabled?: boolean;
}
export function Button({ variant = 'primary', children, onClick, disabled }: ButtonProps) {
return (
<button
className={`btn btn-${variant}`}
onClick={onClick}
disabled={disabled}
>
{children}
</button>
);
}
// packages/ui/package.json
{
"name": "@myapp/ui",
"version": "0.0.0",
"main": "./src/index.ts",
"types": "./src/index.ts",
"exports": {
".": "./src/index.ts"
}
}
// apps/web/package.json
{
"dependencies": {
"@myapp/ui": "*"
}
}
远程缓存
# 登录到 Turbo 远程缓存
npx turbo login
# 链接到远程缓存
npx turbo link
# 或使用 Vercel 远程缓存
# 或使用 turbo-remote-cache 包自托管
# 使用远程缓存构建
turbo build --remote-only
任务命令
# 运行所有构建(并行,带缓存)
turbo build
# 运行特定应用的开发服务器
turbo dev --filter=web
# 仅对更改的包运行测试
turbo test --filter=[HEAD^1]
# 在所有匹配模式的包中运行
turbo build --filter=./packages/*
# 空运行(显示将要运行的内容)
turbo build --dry
# 强制重新构建(忽略缓存)
turbo build --force
共享 TypeScript 配置
// packages/config/tsconfig/base.json
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2022"],
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"skipLibCheck": true,
"isolatedModules": true,
"resolveJsonModule": true
}
}
// apps/web/tsconfig.json
{
"extends": "@myapp/config/tsconfig/nextjs.json",
"include": [".", "next-env.d.ts"],
"exclude": ["node_modules"]
}
Turborepo 的增量构建在远程缓存预热后可将 CI 时间减少 80% 以上。