Skip to content
返回构建教程

Monorepo 搭建

用 pnpm/Turborepo workspace 组织 apps + packages,共享构建缓存。

为什么

当你有 2 个以上 app 或 4 个以上共享包时,多仓要发布私有包才能 import——这个税拖死迭代速度。Monorepo 让你在单个 PR 里跨 app 边界重构,只跑受影响的测试,共享单个 lockfile 防止依赖漂移。盈亏平衡点出奇地早:即使两个 app 共享一个 UI kit 和一个 types 包,每周也能省一整天。代价是工具复杂度,Turborepo/Nx 替你吸收了。

何时使用

当你有 ≥2 个 app 或 ≥4 个共享包时。搭建 Turborepo 约 1-2 小时。单个 app 无共享代码可跳过。

如何做

1. 初始化 pnpm workspaces
   - 根目录 pnpm init;创建 pnpm-workspace.yaml:
     packages:
       - "apps/*"
       - "packages/*"
   - 创建 apps/ 和 packages/ 目录

2. 加 Turborepo
   - pnpm add -Dw turbo
   - 创建 turbo.json,为 build/dev/lint/test 配 pipeline
   - 每个任务声明 "dependsOn" 和 "outputs" 用于缓存

3. 创建第一个共享包(如 packages/ui)
   - 内部 pnpm init;命名为 "@myorg/ui"
   - 加 "exports" 字段,使其可按 "@myorg/ui/button" 导入
   - 用 tsup 或 tsc 构建;"main"/"types" 指向 dist

4. 让 app 消费它
   - 在 apps/web:pnpm add @myorg/ui(自动用 workspace: 协议)
   - import { Button } from "@myorg/ui"——跨包改动热重载

5. 加远程缓存(可选但高杠杆)
   - 启用 Vercel Remote Cache 或自托管 turbo-cache
   - CI 拉取缓存构建产物 → PR 检查快 5-10 倍

示例

// turbo.json
{
  "$schema": "https://turbo.build/schema.json",
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**", ".next/**"]
    },
    "test": { "dependsOn": ["build"] },
    "lint": {},
    "dev": { "cache": false, "persistent": true }
  }
}

# 只跑受变更影响的任务
turbo run build --filter=...[origin/main]
turbo run test --filter=@myorg/web

常见问题

  • ⚠忘了 '^build' 依赖,导致包在消费者之前并行构建——你得到过时的类型错误。
  • ⚠没正确设置 'outputs',Turbo 什么都不缓存,速度优势全失。
  • ⚠用 npm/yaml 而非 pnpm——提升模型破坏 workspace: 协议,出现幽灵依赖。
  • ⚠把所有包放一个 tsconfig 用路径别名而非真实包边界——重构变成全局搜索。

最佳实践

  • ✓所有内部依赖用 workspace: 协议,防止 pnpm 意外把私有包发布到 npm。
  • ✓保留一个根 lockfile——永不提交各包的 lockfile,它们导致 monorepo 内版本漂移。
  • ✓每个包用一致的 scope(@myorg/*)标记,让内部 vs 外部依赖一目了然。
  • ✓Docker 构建前跑 'turbo prune',只打包目标 app 需要的包子集。