当项目规模从单一应用增长到多端(后端 API、管理后台、移动端)时,代码如何组织就成了一个绕不开的问题。我们在实际项目中采用了 Turborepo 来管理多应用 Monorepo,积累了一些值得分享的经验。
为什么选择 Monorepo
传统的多仓库(Polyrepo)模式下,共享代码靠发 npm 包,版本同步是噩梦。假设后端定义了一个订单接口的类型,前端要用,你需要:发包 → 升版本 → 各项目安装 → 祈祷没有遗漏。
Monorepo 把所有项目放在一个仓库里,共享代码变成了直接引用:
monorepo/
├── apps/
│ ├── api/ # 后端服务
│ ├── admin/ # 管理后台
│ └── mobile/ # 移动端
├── packages/
│ ├── shared/ # 共享类型与常量
│ └── api-client/ # 类型安全的 HTTP 客户端
├── turbo.json
└── package.json
改一个类型定义,所有消费方立即生效,不需要发包流程。
Turborepo 的核心价值
Turborepo 解决的核心问题是构建效率。在 Monorepo 中,你不希望改了一行后端代码,前端也跟着重新构建。
任务编排与缓存
turbo.json 定义了任务之间的依赖关系:
{
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**"]
},
"dev": {
"cache": false,
"persistent": true
},
"lint": {},
"test": {
"dependsOn": ["build"]
}
}
}
"dependsOn": ["^build"] 表示构建当前包之前,先构建它依赖的其他包。Turbo 会自动分析依赖图,并行执行不相互依赖的任务。
关键的是缓存机制——如果输入文件没有变化,Turbo 直接跳过构建,从缓存中恢复输出。在我们的项目中,这让 CI 构建时间从 8 分钟降到了 2 分钟左右。
选择性构建
日常开发中,你通常只关心某一个应用:
# 只构建 admin 及其依赖
turbo build --filter=admin
# 只运行 api 的开发服务器
turbo dev --filter=api
--filter 是日常使用频率最高的参数,避免了每次都全量构建。
工作区依赖管理
使用 pnpm 的 workspace:* 协议管理内部依赖:
{
"dependencies": {
"@myapp/shared": "workspace:*",
"@myapp/api-client": "workspace:*"
}
}
这里有一个容易踩的坑:共享包必须有正确的 exports 配置。如果 packages/shared 没有在 package.json 中声明入口,消费方会报模块找不到:
{
"name": "@myapp/shared",
"main": "./src/index.ts",
"types": "./src/index.ts",
"exports": {
".": "./src/index.ts"
}
}
类型安全的 API 客户端
Monorepo 最大的红利之一是跨应用的类型安全。我们把 API 的请求/响应类型放在 packages/shared 中,再用一个统一的 HTTP 客户端包装:
// packages/api-client/src/index.ts
import type { OrderResponse, CreateOrderRequest } from '@myapp/shared';
export async function createOrder(data: CreateOrderRequest): Promise<OrderResponse> {
const res = await fetch('/api/orders', {
method: 'POST',
body: JSON.stringify(data),
});
return res.json();
}
后端改了字段,TypeScript 编译器会在前端代码中立即报错,而不是等到运行时才发现。
实践中的注意事项
1. 不要过度拆包。如果一段代码只有一个消费方,就别抽成独立包。包的管理本身有成本,只有真正被多方共享时才值得。
2. Docker 构建需要特殊处理。Monorepo 的 Docker 镜像构建不能简单地 COPY . .,否则每个应用的镜像都会包含所有代码。推荐使用 turbo prune 生成精简的构建上下文:
turbo prune --scope=api --docker
3. CI 中启用远程缓存。本地缓存只对个人有效,团队协作需要远程缓存。Vercel 提供了开箱即用的方案,也可以自建缓存服务器。
总结
Monorepo 不是银弹,但当你的项目确实需要多应用共享代码时,Turborepo 提供了一个务实的解决方案。它的核心优势在于:依赖图感知的任务编排、增量构建缓存、以及简洁的配置方式。如果你的团队正在被多仓库的版本同步问题困扰,不妨试试这个方案。