当项目从一个包拆为多个包,每个独立 repo 会导致依赖版本不一致、代码复用难、发布链路繁琐。Monorepo 用工具链解决这些痛点,Turborepo + pnpm 是目前体验最优组合。
pnpm workspace 依赖管理
在 pnpm-workspace.yaml 中声明 apps/* 和 packages/* 目录结构,根目录 pnpm install 自动硬链接,同一依赖跨包共享,node_modules 严格隔离避免幽灵依赖。workspace: 协议引用内部包,修改即时生效无需发版。
# pnpm-workspace.yaml
packages:
- 'apps/*'
- 'packages/*'
- 'tooling/*'
# turbo.json
{
"$schema": "https://turbo.build/schema.json",
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": [".next/**", "dist/**"],
"cache": true
},
"typecheck": { "dependsOn": ["^build"], "outputs": [] },
"lint": { "dependsOn": ["^build"], "outputs": [] },
"test": {
"dependsOn": ["^build"],
"outputs": ["coverage/**"],
"inputs": ["**/*.test.*", "jest.config.*"]
},
"dev": { "cache": false, "persistent": true }
}
}
# root package.json
{
"name": "my-monorepo",
"private": true,
"packageManager": "pnpm@9.0.0",
"scripts": {
"build": "turbo build",
"typecheck": "turbo typecheck",
"test": "turbo test",
"dev": "turbo dev",
"release": "changeset version && changeset publish"
},
"devDependencies": {
"turbo": "^2.0.0",
"@changesets/cli": "^2.27.0",
"typescript": "^5.4.0"
}
}
# packages/ui/package.json
{
"name": "@company/ui",
"version": "1.2.0",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" },
"./styles.css": "./dist/styles.css"
},
"sideEffects": false
}
Turborepo 增量构建与缓存
turbo.json 声明任务依赖图:子包 build 依赖上游^build,自动拓扑排序。每次构建的输入哈希命中本地或远程缓存直接跳过,CI 构建速度可提升 5-20 倍。
| 维度 | 手工多仓 | Lerna + yarn | pnpm + Turborepo | pnpm + Nx |
|---|---|---|---|---|
| 依赖去重 | 跨仓各装 | 中等 hoisting | 硬链接共享 | 与Turborepo相同 |
| 任务编排 | 脚本 | 弱并行lerna run | DAG智能缓存 | targetDefaults |
| 远程缓存 | 无 | 付费企业版 | 自建S3/Vercel免费 | Nx Cloud免费额度 |
| 幽灵依赖风险 | 低 | 高 | 极低 | 极低 |
最佳实践
内部包统一 @scope/pkg 命名,Changesets 管理版本与 changelog,禁止跨包相对路径 import。远程缓存配团队 S3 存储,CI 共享加速全员构建。