版本说明
本文档基于以下版本编写:
核心工具版本:
- Turborepo: v2.6.1 (2024 年 11 月发布)
- pnpm: v9.15.0 (2024 年 12 月发布)
- Node.js: 18.0.0+ (推荐 20.18.1 LTS)
重要里程碑:
- ✅ Turborepo 2.0 (2024-06-04):新终端 UI、Watch 模式、MIT 许可证
- ✅ Turborepo 2.1 (2024-07):改进的任务依赖和缓存
- ✅ Turborepo 2.4 (2024-09):性能优化和稳定性改进
- ✅ Turborepo 2.6 (2024-11):最新稳定版本
配置变更(v1.x → v2.x):
- 🔄
pipeline→tasks:配置键名变更 - 🔄
$schemaURL 更新为 v2 - ⚠️ 必须配置
packageManager字段 - ✅ 环境变量配置稳定化
注意事项
- Turborepo 2.x 相比 1.x 有重大变更,建议新项目直接使用 2.x
- packageManager 字段:Turborepo 2.0+ 要求在根 package.json 中定义(如
"packageManager": "pnpm@9.15.0") - 迁移工具:可使用
npx @turbo/codemod migrate自动迁移 1.x 配置到 2.x - pnpm 9.x:引入了新的依赖解析算法,性能更优
什么是 Monorepo #
Monorepo(Monolithic Repository)是一种项目管理策略,将多个相关的项目或包存放在同一个代码仓库中。
传统项目结构(Multi-repo) #
organization/
├── project-a/ (独立仓库)
│ ├── .git/
│ └── package.json
├── project-b/ (独立仓库)
│ ├── .git/
│ └── package.json
└── project-c/ (独立仓库)
├── .git/
└── package.jsonMonorepo 项目结构 #
my-monorepo/
├── .git/ (单一仓库)
├── package.json (根配置)
├── packages/
│ ├── package-a/
│ │ └── package.json
│ ├── package-b/
│ │ └── package.json
│ └── package-c/
│ └── package.jsonMonorepo 的优势 #
✅ 优点:
- 代码共享:包之间可以直接引用,无需发布到 npm
- 统一管理:统一的依赖版本、构建配置、代码规范
- 原子提交:跨包的修改可以在一次提交中完成
- 重构便利:重构影响多个包时更容易追踪和测试
- 协作效率:团队成员可以看到完整的项目代码
❌ 缺点:
- 仓库体积:随着项目增多,仓库会变得很大
- 权限控制:难以对不同包设置不同的访问权限
- CI/CD 复杂:需要智能构建,避免每次都构建所有包
- 学习成本:需要了解 Monorepo 工具和工作流
适用场景 #
适合使用 Monorepo:
- 组件库 + 文档站点
- 前端应用 + 后端 API
- 多个相互依赖的包
- 微前端架构
- 共享工具库的多个应用
不适合使用 Monorepo:
- 完全独立的项目
- 团队规模很小(1-2人)
- 没有代码共享需求
技术选型 #
1. npm workspaces #
特点:
- ✅ npm 7+ 原生支持,无需额外工具
- ✅ 简单易用,适合小型项目
- ❌ 功能相对基础
适用场景:小型 Monorepo,简单的依赖管理
2. pnpm workspaces #
特点:
- ✅ 节省磁盘空间(硬链接)
- ✅ 安装速度快
- ✅ 严格的依赖隔离
- ✅ 功能完善
适用场景:推荐首选,适合各种规模的项目
3. Yarn workspaces #
特点:
- ✅ 成熟稳定
- ✅ 功能丰富
- ❌ Yarn 1 和 Yarn 2+ 差异较大
适用场景:已使用 Yarn 的项目
4. Lerna #
特点:
- ✅ 老牌 Monorepo 工具
- ✅ 提供版本管理和发布功能
- ⚠️ 维护不太活跃
适用场景:需要独立版本管理的多包项目
5. Turborepo #
特点:
- ✅ 智能任务缓存
- ✅ 并行构建优化
- ✅ 远程缓存
- ❌ 需要学习配置
适用场景:大型项目,需要构建优化
6. Nx #
特点:
- ✅ 功能最强大
- ✅ 可视化依赖图
- ✅ 智能构建
- ❌ 学习曲线陡峭
适用场景:企业级大型项目
推荐方案 #
| 项目规模 | 推荐方案 | 理由 |
|---|---|---|
| 小型(2-5个包) | pnpm workspaces | 简单快速,功能够用 |
| 中型(5-10个包) | pnpm + Turborepo | 构建优化,提高效率 |
| 大型(10+个包) | pnpm + Nx | 完整的工具链和优化 |
本文将使用 pnpm workspaces + Turborepo 作为示例。
准备工作 #
1. 安装 Node.js #
# 检查版本
node -v # 需要 v18.0.0 或更高
npm -v # 需要 v9.0.0 或更高如果版本不够,请访问 nodejs.org 下载最新版本。
2. 安装 pnpm #
# 通过 npm 安装
npm install -g pnpm
# 检查版本
pnpm -v # 需要 v8.0.0 或更高3. 创建项目目录 #
# 创建并进入项目目录
mkdir my-monorepo
cd my-monorepo
# 初始化 Git
git init
# 创建 .gitignore
cat > .gitignore << EOF
node_modules
dist
.DS_Store
*.log
.turbo
.env.local
EOF第一步:初始化根目录 #
1.1 创建 package.json #
pnpm init编辑 package.json:
{
"name": "my-monorepo",
"version": "1.0.0",
"private": true,
"description": "My awesome monorepo project",
"scripts": {
"dev": "pnpm --parallel --recursive run dev",
"build": "pnpm --recursive run build",
"lint": "pnpm --recursive run lint",
"test": "pnpm --recursive run test",
"clean": "pnpm --recursive run clean && rm -rf node_modules"
},
"keywords": ["monorepo"],
"author": "Your Name",
"license": "MIT",
"engines": {
"node": ">=18.0.0",
"pnpm": ">=8.0.0"
}
}说明:
"private": true- 防止根目录被发布到 npm--recursive- 在所有子包中执行命令--parallel- 并行执行(适合开发模式)
1.2 配置 pnpm workspace #
创建 pnpm-workspace.yaml:
packages:
# 所有在 packages 目录下的包
- 'packages/*'
# 所有在 apps 目录下的应用
- 'apps/*'
# 排除测试目录
- '!**/test/**'
- '!**/node_modules/**'配置说明:
- ✅
packages/*- 通常存放可复用的包(库、组件、工具) - ✅
apps/*- 通常存放应用程序(网站、服务) - ✅
!**/test/**- 排除测试目录(避免作为独立包) - ✅
!**/node_modules/**- 排除依赖目录
高级配置示例:
packages:
# 精确匹配单个包
- 'my-app'
# packages 目录下的直接子目录
- 'packages/*'
# packages 目录下的所有子目录(递归)
- 'packages/**'
# 多个目录
- 'apps/*'
- 'packages/*'
- 'tools/*'
# 排除模式
- '!**/test/**'
- '!**/*.test.ts'
- '!**/node_modules/**'pnpm 9.x 新特性:
# pnpm-workspace.yaml
packages:
- 'packages/*'
- 'apps/*'
# catalog(pnpm 9.0+):统一管理依赖版本
catalog:
react: ^18.3.0
typescript: ^5.3.3
vite: ^5.0.0使用 catalog:
{
"dependencies": {
"react": "catalog:",
"typescript": "catalog:"
}
}1.3 创建目录结构 #
# 创建目录
mkdir -p packages apps
# 创建基础结构
mkdir -p packages/shared
mkdir -p packages/ui
mkdir -p apps/web
mkdir -p apps/docs最终结构:
my-monorepo/
├── .git/
├── .gitignore
├── package.json
├── pnpm-workspace.yaml
├── packages/ # 共享包
│ ├── shared/ # 共享工具
│ └── ui/ # UI 组件库
└── apps/ # 应用
├── web/ # 主应用
└── docs/ # 文档站点第二步:创建共享包 #
2.1 创建 shared 包 #
cd packages/shared
pnpm init编辑 packages/shared/package.json:
{
"name": "@my-monorepo/shared",
"version": "1.0.0",
"description": "Shared utilities and helpers",
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.js"
}
},
"files": [
"dist"
],
"scripts": {
"dev": "tsup src/index.ts --watch --format cjs,esm --dts",
"build": "tsup src/index.ts --format cjs,esm --dts",
"clean": "rm -rf dist"
},
"devDependencies": {
"tsup": "^8.0.0",
"typescript": "^5.3.3"
}
}创建源代码 packages/shared/src/index.ts:
/**
* 格式化日期
*/
export function formatDate(date: Date): string {
return date.toLocaleDateString('zh-CN');
}
/**
* 延迟函数
*/
export function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
/**
* 生成随机ID
*/
export function generateId(): string {
return Math.random().toString(36).substring(2, 15);
}创建 packages/shared/tsconfig.json:
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"lib": ["ES2020"],
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "bundler"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}2.2 创建 UI 组件库 #
cd ../../packages/ui
pnpm init编辑 packages/ui/package.json:
{
"name": "@my-monorepo/ui",
"version": "1.0.0",
"description": "Shared UI components",
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.js"
},
"./style.css": "./dist/style.css"
},
"files": [
"dist"
],
"scripts": {
"dev": "tsup src/index.ts --watch --format cjs,esm --dts",
"build": "tsup src/index.ts --format cjs,esm --dts",
"clean": "rm -rf dist"
},
"dependencies": {
"@my-monorepo/shared": "workspace:*"
},
"devDependencies": {
"tsup": "^8.0.0",
"typescript": "^5.3.3"
}
}注意:"@my-monorepo/shared": "workspace:*" 表示引用本地 workspace 中的包。
创建组件 packages/ui/src/Button.ts:
import { generateId } from '@my-monorepo/shared';
export interface ButtonProps {
text: string;
onClick?: () => void;
}
export class Button {
private id: string;
private element: HTMLButtonElement;
constructor(props: ButtonProps) {
this.id = generateId();
this.element = document.createElement('button');
this.element.textContent = props.text;
this.element.id = this.id;
if (props.onClick) {
this.element.addEventListener('click', props.onClick);
}
}
render(container: HTMLElement): void {
container.appendChild(this.element);
}
}创建入口 packages/ui/src/index.ts:
export { Button } from './Button';
export type { ButtonProps } from './Button';创建 packages/ui/tsconfig.json:
{
"extends": "../shared/tsconfig.json",
"compilerOptions": {
"lib": ["ES2020", "DOM"],
"rootDir": "./src"
}
}2.3 安装依赖 #
回到根目录:
cd ../..
# 安装所有依赖
pnpm install第三步:创建应用 #
3.1 创建 Web 应用(Vite + Vue 3) #
cd apps/web
# 使用 Vite 创建 Vue 3 项目
pnpm create vite . --template vue-ts编辑 apps/web/package.json:
{
"name": "@my-monorepo/web",
"version": "1.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vue-tsc && vite build",
"preview": "vite preview"
},
"dependencies": {
"vue": "^3.4.0",
"@my-monorepo/shared": "workspace:*",
"@my-monorepo/ui": "workspace:*"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.0.0",
"typescript": "^5.3.3",
"vite": "^5.0.0",
"vue-tsc": "^1.8.0"
}
}编辑 apps/web/vite.config.ts:
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
export default defineConfig({
plugins: [vue()],
server: {
port: 3000
}
});创建示例页面 apps/web/src/App.vue:
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { formatDate, generateId } from '@my-monorepo/shared';
const currentDate = ref('');
const uniqueId = ref('');
onMounted(() => {
currentDate.value = formatDate(new Date());
uniqueId.value = generateId();
});
</script>
<template>
<div class="app">
<h1>My Monorepo Web App</h1>
<p>Current Date: {{ currentDate }}</p>
<p>Unique ID: {{ uniqueId }}</p>
</div>
</template>
<style scoped>
.app {
padding: 2rem;
font-family: sans-serif;
}
h1 {
color: #42b883;
}
</style>3.2 创建文档站点(VitePress) #
cd ../docs
# 初始化 VitePress
pnpm init
pnpm add -D vitepress vue编辑 apps/docs/package.json:
{
"name": "@my-monorepo/docs",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "vitepress dev",
"build": "vitepress build",
"preview": "vitepress preview"
},
"devDependencies": {
"vitepress": "^1.0.0",
"vue": "^3.4.0"
}
}创建 apps/docs/.vitepress/config.ts:
import { defineConfig } from 'vitepress';
export default defineConfig({
title: 'My Monorepo Docs',
description: 'Documentation for my monorepo project',
themeConfig: {
nav: [
{ text: 'Home', link: '/' },
{ text: 'Guide', link: '/guide/' }
],
sidebar: [
{
text: 'Guide',
items: [
{ text: 'Getting Started', link: '/guide/' },
{ text: 'Shared Utils', link: '/guide/shared' },
{ text: 'UI Components', link: '/guide/ui' }
]
}
]
}
});创建 apps/docs/index.md:
---
layout: home
title: Home
---
# My Monorepo
Welcome to the documentation!
## Features
- 🚀 Fast and efficient
- 📦 Well organized
- 🛠️ Easy to maintain创建 apps/docs/guide/index.md:
# Getting Started
This is a monorepo project built with pnpm workspaces.
## Installation
\`\`\`bash
pnpm install
\`\`\`
## Development
\`\`\`bash
pnpm dev
\`\`\`3.3 回到根目录安装依赖 #
cd ../..
pnpm install第四步:配置 Turborepo #
4.1 安装 Turborepo #
pnpm add -D turbo4.2 创建 turbo.json #
在根目录创建 turbo.json(Turborepo 2.x 语法):
{
"$schema": "https://turbo.build/schema.json",
"ui": "tui",
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**", "!.next/cache/**"]
},
"dev": {
"cache": false,
"persistent": true
},
"lint": {
"dependsOn": ["^build"]
},
"test": {
"dependsOn": ["^build"],
"outputs": ["coverage/**"]
},
"clean": {
"cache": false
}
}
}Turborepo 2.x 配置说明:
- ✅
tasks替代了 v1.x 的pipeline - ✅
ui: "tui"- 启用新的终端 UI(v2.0 新特性) - ✅
dependsOn: ["^build"]-^表示先构建上游依赖的包 - ✅
outputs- 指定缓存的输出目录,支持排除模式(!前缀) - ✅
cache: false- 禁用缓存(适用于 dev 和 clean) - ✅
persistent: true- 标记为持续运行的任务(如 dev server)
v1.x → v2.x 迁移:
// ❌ Turborepo 1.x(旧语法)
{
"pipeline": {
"build": { ... }
}
}
// ✅ Turborepo 2.x(新语法)
{
"tasks": {
"build": { ... }
}
}自动迁移命令:
# 自动迁移配置到 Turborepo 2.x
npx @turbo/codemod migrate
# 单独迁移特定项
npx @turbo/codemod update-schema-json-url # 更新 schema URL
npx @turbo/codemod migrate-dot-env # 迁移 dotEnv 配置
npx @turbo/codemod migrate-env-var-dependencies # 迁移环境变量依赖4.3 更新根目录脚本 #
编辑根目录 package.json(Turborepo 2.x 要求):
{
"name": "my-monorepo",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "turbo run dev",
"build": "turbo run build",
"lint": "turbo run lint",
"test": "turbo run test",
"clean": "turbo run clean && rm -rf node_modules .turbo"
},
"devDependencies": {
"turbo": "^2.6.1"
},
"engines": {
"node": ">=18.0.0",
"pnpm": ">=9.0.0"
},
"packageManager": "pnpm@9.15.0"
}重要配置项:
✅
packageManager(Turborepo 2.0+ 必需):指定包管理器和精确版本- 格式:
"<manager>@<version>" - 示例:
"pnpm@9.15.0"或"npm@10.9.2" - 作用:确保团队使用相同的包管理器版本,提高构建一致性
- 格式:
✅
turbo: "^2.6.1":使用 Turborepo 2.x 最新稳定版- v2.0+:新 UI、Watch 模式、MIT 许可证
- v2.6.1:最新性能优化和 bug 修复
✅
engines:指定运行环境要求- Node.js 18+ 是 Turborepo 2.x 推荐版本
- pnpm 9.0+ 支持最新特性
## 第五步:配置代码规范
### 5.1 安装 ESLint 和 Prettier
```bash
pnpm add -D eslint prettier \
@typescript-eslint/eslint-plugin \
@typescript-eslint/parser \
eslint-config-prettier \
eslint-plugin-prettier5.2 创建 .eslintrc.cjs #
module.exports = {
root: true,
env: {
browser: true,
es2021: true,
node: true
},
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended'
],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 2021,
sourceType: 'module'
},
plugins: ['@typescript-eslint'],
rules: {
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/no-unused-vars': ['error', {
argsIgnorePattern: '^_',
varsIgnorePattern: '^_'
}]
}
};5.3 创建 .prettierrc.json #
{
"semi": true,
"singleQuote": true,
"printWidth": 100,
"tabWidth": 2,
"trailingComma": "es5",
"arrowParens": "always",
"endOfLine": "lf"
}5.4 创建 .eslintignore 和 .prettierignore #
cat > .eslintignore << EOF
node_modules
dist
.turbo
*.config.js
*.config.ts
EOF
cp .eslintignore .prettierignore5.5 添加 lint 脚本 #
在各个包的 package.json 中添加:
{
"scripts": {
"lint": "eslint . --ext .ts,.tsx,.js,.jsx,.vue"
}
}第六步:配置 Git Hooks #
6.1 安装 Husky 和 lint-staged #
pnpm add -D husky lint-staged
npx husky init6.2 配置 lint-staged #
在根目录 package.json 中添加:
{
"scripts": {
"prepare": "husky"
},
"lint-staged": {
"*.{js,jsx,ts,tsx,vue}": [
"eslint --fix",
"prettier --write"
],
"*.{json,md,yml,yaml}": [
"prettier --write"
]
}
}6.3 配置 pre-commit hook #
编辑 .husky/pre-commit:
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx lint-staged第七步:配置 TypeScript #
7.1 创建根目录 tsconfig.json #
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"lib": ["ES2020"],
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "bundler",
"resolveJsonModule": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true
}
}7.2 各包继承根配置 #
在packages的 tsconfig.json 中:
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src",
"moduleResolution": "bundler",
"lib": [
"ES2020",
"DOM"
]
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules",
"dist"
]
}第八步:测试和构建 #
8.1 构建所有包 #
# 构建 shared 包
cd packages/shared
pnpm build
# 构建 ui 包
cd ../ui
pnpm build
# 回到根目录
cd ../..8.2 启动开发服务器 #
# 启动所有 dev 服务器
pnpm dev
# 或单独启动
pnpm --filter @my-monorepo/web dev
pnpm --filter @my-monorepo/docs dev访问:
- Web 应用:http://localhost:3000
- 文档站点:http://localhost:5173
8.3 构建所有项目 #
pnpm build完整目录结构 #
my-monorepo/
├── .git/
├── .gitignore
├── .eslintrc.cjs
├── .eslintignore
├── .prettierrc.json
├── .prettierignore
├── .husky/
│ └── pre-commit
├── package.json
├── pnpm-workspace.yaml
├── pnpm-lock.yaml
├── turbo.json
├── tsconfig.json
├── packages/
│ ├── shared/
│ │ ├── src/
│ │ │ └── index.ts
│ │ ├── dist/
│ │ ├── package.json
│ │ └── tsconfig.json
│ └── ui/
│ ├── src/
│ │ ├── Button.ts
│ │ └── index.ts
│ ├── dist/
│ ├── package.json
│ └── tsconfig.json
└── apps/
├── web/
│ ├── src/
│ │ ├── App.vue
│ │ └── main.ts
│ ├── public/
│ ├── index.html
│ ├── package.json
│ ├── tsconfig.json
│ └── vite.config.ts
└── docs/
├── .vitepress/
│ └── config.ts
├── guide/
│ ├── index.md
│ ├── shared.md
│ └── ui.md
├── index.md
└── package.json常用命令 #
依赖管理 #
# 安装所有依赖
pnpm install
# 添加根目录依赖
pnpm add -D typescript -w
# 为特定包添加依赖
pnpm --filter @my-monorepo/web add vue-router
# 为所有包添加依赖
pnpm --recursive add lodash
# 删除依赖
pnpm --filter @my-monorepo/web remove axios执行脚本 #
# 在所有包中执行
pnpm --recursive run build
# 并行执行
pnpm --parallel --recursive run dev
# 在特定包中执行
pnpm --filter @my-monorepo/web dev
# 在多个包中执行
pnpm --filter @my-monorepo/web --filter @my-monorepo/docs dev使用 Turborepo #
# 构建(会自动处理依赖顺序)
turbo run build
# 开发模式
turbo run dev
# Watch 模式(Turborepo 2.0 新特性)
turbo watch dev
# 自动检测文件变化并重新运行任务
# 只构建特定包及其依赖
turbo run build --filter=@my-monorepo/web
# 强制重新构建(忽略缓存)
turbo run build --force
# 查看依赖图
turbo run build --graph
# 查看任务执行摘要
turbo run build --summarizeTurborepo 2.0 新功能:
1. Watch 模式:
# 监听文件变化,自动重新运行
turbo watch dev
turbo watch build
turbo watch lint
# 等价于传统的 nodemon/chokidar,但使用 Turborepo 的依赖图2. 新终端 UI:
# 启用交互式 TUI(默认启用)
turbo run dev --ui=tui
# 使用传统流式输出
turbo run dev --ui=stream3. 任务过滤:
# 只运行指定包
turbo run build --filter=@my-monorepo/web
# 运行多个包
turbo run build --filter=@my-monorepo/web --filter=@my-monorepo/docs
# 运行包及其依赖
turbo run build --filter=@my-monorepo/web...
# 运行包及其依赖者
turbo run build --filter=...@my-monorepo/shared清理 #
# 清理所有 dist
pnpm clean
# 清理所有 node_modules
pnpm --recursive exec rm -rf node_modules
rm -rf node_modules
# 重新安装
pnpm install发布流程 #
1. 配置发布脚本 #
在需要发布的包中添加:
{
"scripts": {
"prepublishOnly": "pnpm build"
},
"publishConfig": {
"access": "public"
}
}2. 发布单个包 #
cd packages/shared
pnpm publish3. 批量发布(使用 Changesets) #
安装 Changesets:
pnpm add -D @changesets/cli
pnpm changeset init创建 changeset:
pnpm changeset发布:
# 更新版本
pnpm changeset version
# 构建
pnpm build
# 发布
pnpm changeset publish最佳实践 #
1. 命名规范 #
包命名:@scope/package-name
├── @my-monorepo/shared ✅ 共享工具
├── @my-monorepo/ui ✅ UI 组件
├── @my-monorepo/web ✅ Web 应用
└── @my-monorepo/docs ✅ 文档站点2. 依赖引用 #
{
"dependencies": {
"@my-monorepo/shared": "workspace:*", // 使用最新版本
"@my-monorepo/ui": "workspace:^1.0.0" // 指定版本范围
}
}3. 构建顺序 #
确保依赖的包先构建(Turborepo 2.x 语法):
// turbo.json
{
"tasks": {
"build": {
"dependsOn": ["^build"] // ^ 表示依赖的包
}
}
}依赖语法说明:
{
"tasks": {
"build": {
// ✅ ^build - 先运行依赖包的 build
"dependsOn": ["^build"]
},
"test": {
// ✅ build - 先运行当前包的 build
// ✅ ^build - 然后运行依赖包的 build
"dependsOn": ["build", "^build"]
},
"deploy": {
// ✅ build, test - 按顺序运行当前包的任务
"dependsOn": ["build", "test"]
}
}
}4. 版本管理策略 #
统一版本(Unified):
- 所有包使用相同版本
- 适合紧密关联的包
- 示例:Babel, Vue 3
独立版本(Independent):
- 每个包独立版本
- 适合松散关联的包
- 示例:Lodash
5. Git 提交规范 #
使用 Conventional Commits:
feat(web): 添加用户登录功能
fix(shared): 修复日期格式化 bug
docs(ui): 更新 Button 组件文档
chore: 升级依赖版本6. CI/CD 配置 #
创建 .github/workflows/ci.yml:
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: pnpm/action-setup@v2
with:
version: 8
- uses: actions/setup-node@v3
with:
node-version: 18
cache: 'pnpm'
- name: Install dependencies
run: pnpm install
- name: Lint
run: pnpm lint
- name: Build
run: pnpm build
- name: Test
run: pnpm test7. 性能优化 #
使用 Turborepo 缓存(v2.x 语法):
{
"tasks": {
"build": {
"outputs": ["dist/**", ".next/**", "!.next/cache/**"],
"dependsOn": ["^build"],
"inputs": ["$TURBO_DEFAULT$", ".env*"]
}
}
}缓存优化技巧:
- ✅ 精确的 outputs:只缓存必要的文件,减少缓存体积
- ✅ 排除模式:使用
!排除不需要缓存的文件(如.next/cache/**) - ✅ inputs 配置:指定影响缓存的输入文件
- ✅ 环境变量:使用
env配置影响缓存的环境变量
{
"tasks": {
"build": {
"outputs": ["dist/**"],
"env": ["DATABASE_URL", "API_KEY"], // 环境变量影响缓存
"inputs": ["src/**/*.ts", "!src/**/*.test.ts"] // 测试文件不影响构建缓存
}
}
}使用 pnpm 的并行安装:
pnpm install --parallel配置 .npmrc(pnpm 9.x 推荐):
# .npmrc
# 提升依赖到根 node_modules(提高兼容性)
shamefully-hoist=true
# 不严格检查 peer 依赖(避免冲突)
strict-peer-dependencies=false
# 自动安装 peer dependencies(pnpm 9.x)
auto-install-peers=true
# 使用符号链接(节省空间)
symlink=trueTurborepo 远程缓存:
# 登录 Vercel(免费提供远程缓存)
npx turbo login
# 链接项目
npx turbo link
# 之后所有构建都会使用远程缓存
turbo run build远程缓存优势:
- ✅ 团队成员共享缓存
- ✅ CI/CD 加速构建
- ✅ 跨设备一致性
常见问题 #
1. 包引用失败 #
问题:导入本地包时报错找不到模块
解决方案:
# 确保已构建依赖的包
pnpm --filter @my-monorepo/shared build
# 重新安装依赖
pnpm install2. TypeScript 类型找不到 #
问题:TypeScript 无法找到本地包的类型
解决方案:
确保包的 package.json 中配置了 types 字段:
{
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts"
}
}
}3. 循环依赖 #
问题:包 A 依赖包 B,包 B 又依赖包 A
解决方案:
- 重新设计包结构,提取共享代码到新包
- 使用依赖注入避免直接依赖
4. 构建缓存问题 #
问题:Turborepo 缓存了错误的构建结果
解决方案:
# 清理本地缓存
rm -rf .turbo
# 强制重新构建(忽略缓存)
turbo run build --force
# 清理所有缓存和输出
turbo run clean
pnpm clean缓存调试:
# 查看缓存命中情况
turbo run build --summarize
# 生成缓存摘要文件
turbo run build --summarize=summary.json
# 查看为什么任务被执行(未命中缓存)
turbo run build --dry-run5. Turborepo 2.x 迁移问题 #
问题:从 Turborepo 1.x 升级到 2.x 后配置不工作
解决方案:
# 自动迁移配置
npx @turbo/codemod migrate
# 检查迁移后的配置
cat turbo.json
# 验证配置正确性
turbo run build --dry-run主要变更检查清单:
- ✅
pipeline→tasks - ✅
$schemaURL 更新 - ✅
packageManager字段已添加 - ✅ 环境变量从
experimentalGlobalPassThroughEnv迁移到globalPassThroughEnv
6. pnpm workspace 协议问题 #
问题:使用 workspace:* 后发布到 npm 失败
解决方案:
pnpm 会自动在发布时将 workspace:* 替换为实际版本号。确保:
{
"dependencies": {
"@my-monorepo/shared": "workspace:*" // 开发时
}
}
// 发布后自动转换为:
{
"dependencies": {
"@my-monorepo/shared": "1.0.0" // 发布后
}
}pnpm 发布配置:
{
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org/"
}
}5. pnpm-lock.yaml 冲突 #
问题:多人协作时 pnpm-lock.yaml 经常冲突
解决方案:
# 删除 lock 文件
rm pnpm-lock.yaml
# 重新生成
pnpm install进阶功能 #
1. 共享配置 #
创建 packages/config:
// packages/config/eslint-config/index.js
module.exports = {
extends: ['eslint:recommended'],
rules: {
// 共享规则
}
};在其他包中使用:
{
"eslintConfig": {
"extends": "@my-monorepo/config/eslint-config"
}
}2. 自定义工具包 #
创建 packages/scripts:
// packages/scripts/src/build.ts
export function build() {
console.log('Custom build script');
// 自定义构建逻辑
}3. 远程缓存(Turborepo) #
配置远程缓存加速团队构建:
# 登录 Vercel
npx turbo login
# 链接项目
npx turbo link总结 #
通过本教程,你已经学会了:
✅ 完成的工作 #
项目初始化
- 配置 pnpm 9.x workspaces
- 创建 Monorepo 目录结构
- 配置 catalog 统一依赖版本(pnpm 9.0+)
创建包和应用
- 共享工具包(shared)
- UI 组件库(ui)
- Web 应用(web)
- 文档站点(docs)
构建优化(Turborepo 2.x)
- 配置 Turborepo 2.6.1(最新稳定版)
- 智能缓存和并行构建
- Watch 模式和新终端 UI
- 远程缓存(可选)
代码规范
- ESLint + Prettier
- Git Hooks(Husky + lint-staged)
TypeScript 配置
- 类型检查
- 声明文件生成
🎯 下一步 #
- 添加测试:配置 Vitest 或 Jest
- 添加 E2E 测试:配置 Playwright 或 Cypress
- 配置 CI/CD:GitHub Actions 自动化
- 添加文档:完善各包的 README
- 版本管理:使用 Changesets 管理版本
- 探索 Turborepo 2.x 新特性:Watch 模式、任务过滤、远程缓存
📚 参考资源 #
官方文档:
工具和生态:
迁移指南:
🆕 Turborepo 2.x 关键特性 #
- ✅ 新终端 UI:交互式任务查看和日志
- ✅ Watch 模式:智能文件监听和自动重跑
- ✅ MIT 许可证:从专有许可证变更
- ✅ 长期支持:官方支持政策
- ✅ 性能提升:Rust 核心引擎优化
📊 版本兼容性 #
| 工具 | 推荐版本 | 最低版本 | 备注 |
|---|---|---|---|
| Turborepo | 2.6.1 | 2.0.0 | 使用 2.x 新语法 |
| pnpm | 9.15.0 | 9.0.0 | 支持 catalog 特性 |
| Node.js | 20.18.1 LTS | 18.0.0 | 推荐 LTS 版本 |
| TypeScript | 5.3.3+ | 5.0.0 | 支持最新特性 |
🎉 恭喜!你已经成功创建了一个基于 Turborepo 2.x + pnpm 9.x 的现代化 Monorepo 项目!