正在加载,请稍候…

TypeScript Monorepo 与 Turborepo:共享代码与增量构建

学习使用 Turborepo 构建 TypeScript monorepo,涵盖任务管道、远程缓存、共享包、工作区配置及优化构建性能。

TypeScript Monorepo with Turborepo: Shared Code and Incremental Builds

TypeScript Monorepo with Turborepo

为什么选择 Turborepo?

大型 monorepo 的问题:
  - 构建时间长(重新构建未更改的代码)
  - 复杂的任务依赖(先构建 A 再构建 B)
  - 无法共享构建缓存

Turborepo 的解决方案:
  - 增量构建(仅重新构建更改的包)
  - 远程缓存(在 CI 运行之间共享缓存)
  - 具有依赖感知的并行执行

TypeScript Monorepo with Turborepo: Shared Code and Incremental Builds illustration

设置

# 创建新的 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      (根目录)

TypeScript Monorepo with Turborepo: Shared Code and Incremental Builds illustration

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": "*"
  }
}

TypeScript Monorepo with Turborepo: Shared Code and Incremental Builds illustration

远程缓存

# 登录到 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% 以上。