
使用 Nx 和 Turborepo 的 Monorepo 架构
Monorepo 支持跨多个包共享代码和统一工具。本指南比较 Nx 和 Turborepo,并涵盖生产模式。
为什么使用 Monorepo?
优点:
- 代码共享:共享工具函数、类型和组件
- 原子提交:一次提交更改多个包
- 统一工具:一个 linter、格式化器、TypeScript 配置
- 简化依赖管理:无版本冲突

Turborepo 设置
# 使用 Turborepo 创建新的 monorepo
npx create-turbo@latest my-monorepo
cd my-monorepo
// turbo.json
{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": ["**/.env.*local"],
"pipeline": {
"build": {
"dependsOn": ["^build"], // ^ 表示先运行依赖
"outputs": ["dist/**", ".next/**"],
"cache": true
},
"test": {
"dependsOn": ["build"],
"outputs": ["coverage/**"],
"cache": true
},
"lint": {
"outputs": []
},
"dev": {
"cache": false,
"persistent": true // 长时间运行进程
},
"typecheck": {
"dependsOn": ["^build"],
"outputs": []
}
}
}
工作区结构
my-monorepo/
apps/
web/ # Next.js 前端
api/ # Express 后端
docs/ # 文档站点
packages/
ui/ # 共享 React 组件
types/ # 共享 TypeScript 类型
utils/ # 共享工具函数
config/ # 共享配置(eslint, tsconfig)
package.json # 根工作区配置
turbo.json
// 根 package.json
{
"name": "my-monorepo",
"workspaces": ["apps/*", "packages/*"],
"scripts": {
"build": "turbo run build",
"dev": "turbo run dev",
"test": "turbo run test",
"lint": "turbo run lint",
"typecheck": "turbo run typecheck"
},
"devDependencies": {
"turbo": "latest"
}
}

共享包设置
// packages/ui/package.json
{
"name": "@myapp/ui",
"main": "./src/index.ts",
"types": "./src/index.ts",
"exports": {
".": "./src/index.ts"
},
"scripts": {
"build": "tsup src/index.ts --format cjs,esm --dts",
"dev": "tsup src/index.ts --format cjs,esm --dts --watch",
"lint": "eslint src/",
"typecheck": "tsc --noEmit"
},
"peerDependencies": {
"react": ">=18"
}
}
// packages/ui/src/Button.tsx
export interface ButtonProps {
children: React.ReactNode;
variant?: 'primary' | 'secondary';
onClick?: () => void;
}
export function Button({ children, variant = 'primary', onClick }: ButtonProps) {
return (
<button
className={`btn btn-${variant}`}
onClick={onClick}
>
{children}
</button>
);
}
// packages/ui/src/index.ts
export { Button } from './Button';
export { Input } from './Input';
export { Modal } from './Modal';
// apps/web/src/page.tsx - 使用共享包
import { Button, Input } from '@myapp/ui';
export default function HomePage() {
return (
<div>
<Input placeholder="搜索..." />
<Button variant="primary">提交</Button>
</div>
);
}
共享 TypeScript 配置
// packages/config/tsconfig.base.json
{
"compilerOptions": {
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"moduleResolution": "bundler",
"target": "ES2022",
"lib": ["ES2022"],
"paths": {
"@myapp/*": ["../../packages/*/src"]
}
}
}
// apps/web/tsconfig.json
{
"extends": "@myapp/config/tsconfig.base.json",
"compilerOptions": {
"lib": ["ES2022", "DOM"],
"plugins": [{ "name": "next" }]
},
"include": ["src/**/*", "next-env.d.ts"]
}

Nx Monorepo 替代方案
# 创建 Nx 工作区
npx create-nx-workspace@latest my-workspace
cd my-workspace
# 生成应用和库
nx generate @nx/react:application web
nx generate @nx/express:application api
nx generate @nx/react:library ui --directory=shared
// nx.json
{
"targetDefaults": {
"build": {
"dependsOn": ["^build"],
"cache": true
},
"test": {
"cache": true
}
},
"affected": {
"defaultBase": "main"
}
}
使用远程缓存的 CI 优化
# Turborepo 远程缓存(Vercel)
npx turbo login
npx turbo link # 链接到 Vercel 团队
# GitHub Actions
- name: Build
run: turbo run build
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
# 仅运行受影响的包
turbo run test --filter="...[HEAD^1]"
# 仅自上次提交以来更改的包
内部包版本管理
// packages/ui/package.json
{
"version": "0.0.0", // 内部使用 - 不发布
// 或者使用 changesets 管理可发布的包
}
# 使用 Changesets 进行版本管理
npx changeset init
npx changeset # 为修改的包创建 changeset
npx changeset version # 根据 changesets 更新版本
npx changeset publish # 发布到 npm
总结
Monorepo 最佳实践:
- 对于以 JavaScript 为中心的 monorepo,使用 Turborepo(更简单)
- 对于大型企业代码库,使用 Nx(功能更丰富)
- 通过显式导出定义清晰的包边界
- 共享 tsconfig、eslint 和 prettier 配置
- 使用远程缓存实现快速 CI 构建
- 使用 Changesets 将包发布到 npm