Monorepo 实践:Turborepo 从零到一
多个相关项目放在一个仓库里管理,共享代码、统一构建、依赖一处安装。这就是 Monorepo。
为什么用 Monorepo
- 共享代码 — 公共组件、工具函数不用发 npm 包
- 原子提交 — 一个改动同时改多个包,一起提交
- 统一依赖 — 不会出现版本不一致
- 增量构建 — 只构建改动的包
Turborepo 初始化
1 2 3
| pnpm dlx create-turbo@latest my-monorepo cd my-monorepo pnpm install
|
项目结构
1 2 3 4 5 6 7 8 9 10 11
| monorepo/ ├── apps/ │ ├── web/ │ └── admin/ ├── packages/ │ ├── ui/ │ ├── utils/ │ └── tsconfig/ ├── turbo.json ├── package.json └── pnpm-workspace.yaml
|
pnpm-workspace.yaml
1 2 3
| packages: - 'apps/*' - 'packages/*'
|
共享组件包
1 2 3 4 5 6 7 8 9
| { "name": "@my/ui", "main": "./src/index.ts", "types": "./src/index.ts", "dependencies": { "vue": "^3.5.0" } }
|
1 2 3
| export { default as Button } from './Button.vue' export { default as Modal } from './Modal.vue'
|
1 2 3 4
| <!-- apps/web 中使用 --> <script setup lang="ts"> import { Button, Modal } from '@my/ui' </script>
|
Turborepo 任务配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| { "$schema": "https://turbo.build/schema.json", "tasks": { "build": { "dependsOn": ["^build"], "outputs": ["dist/**"] }, "dev": { "dependsOn": ["^build"], "cache": false }, "lint": {}, "type-check": { "dependsOn": ["^build"] } } }
|
常用命令
1 2 3 4 5 6 7 8 9 10 11
| pnpm turbo build
pnpm turbo build --filter=web
pnpm turbo dev
pnpm turbo clean
|
缓存优化
Turborepo 默认开启本地缓存和远程缓存:
1 2 3 4 5 6
| .turbo/cache/
pnpm turbo login pnpm turbo link
|
构建过一次的包,下次直接用缓存,CI 构建时间从 10 分钟降到 2 分钟。
踩坑记录
- 包之间不要循环依赖 — A 依赖 B,B 就不能再依赖 A
- 版本号统一管理 — 用
changesets 做版本发布
- TypeScript 路径别名 — 每个包配自己的
tsconfig.json
- 不要把所有东西都放 packages — 业务逻辑留在 apps 里