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 需要的包子集。