前端工程Technical Deep Dive

Monorepo 实战 —— Turborepo、PNPM Workspace 与 Nx

发布时间2026/03/29
分类前端工程
预计阅读2 分钟
作者吴长龙
*

什么是 Monorepo?

01.内容

什么是 Monorepo?

Monorepo(单体仓库)是将多个项目放在一个仓库中管理的方式。

对比

  • Multi-repo:每个项目独立仓库
  • Monorepo:所有项目统一仓库

为什么需要 Monorepo?

#### Multi-repo 的痛点

text snippettext
项目A (repo-a)
  └── utils-a
项目B (repo-b)  
  └── utils-b (和 utils-a 类似)
项目C (repo-c)
  └── 需要同时依赖 repo-a 和 repo-b

问题:

  • 代码复用困难
  • 版本管理混乱
  • 跨项目修改麻烦
  • 统一构建/部署复杂

#### Monorepo 的优势

text snippettext
monorepo/
├── packages/
│   ├── utils/      # 共享工具
│   ├── ui/         # 组件库
│   ├── api/        # API 封装
│   └── app-a/      # 应用 A
│   └── app-b/      # 应用 B
├── apps/
│   └── web/
│   └── mobile/
└── packages/
    └── shared/     # 共享逻辑

优势:

  • 代码共享简单
  • 统一依赖管理
  • 原子提交(atomic commit)
  • 一致的开发体验

PNPM Workspace

PNPM 的内置 Monorepo 支持:

yaml snippetyaml
# pnpm-workspace.yaml
packages:
  - 'packages/*'
  - 'apps/*'
json snippetjson
// package.json (根目录)
{
  "name": "my-monorepo",
  "private": true,
  "scripts": {
    "dev": "pnpm --filter web dev",
    "build": "pnpm -r build",
    "test": "pnpm -r test"
  },
  "devDependencies": {
    "typescript": "^5.0.0"
  }
}
json snippetjson
// packages/shared/package.json
{
  "name": "@myorg/shared",
  "version": "1.0.0",
  "main": "./src/index.ts",
  "exports": {
    ".": "./src/index.ts"
  }
}

// packages/ui/package.json
{
  "name": "@myorg/ui",
  "version": "1.0.0",
  "dependencies": {
    "@myorg/shared": "workspace:*"
  }
}

使用

bash snippetbash
# 安装依赖(根目录一次安装)
pnpm install

# 在特定包运行
pnpm --filter @myorg/ui build

# 构建所有
pnpm -r build

Turborepo:构建优化

Turborepo 是 Vercel 出的构建编排工具:

json snippetjson
// package.json
{
  "scripts": {
    "build": "turbo run build",
    "dev": "turbo run dev",
    "lint": "turbo run lint"
  },
  "devDependencies": {
    "turbo": "^1.10.0"
  }
}
json snippetjson
// turbo.json
{
  "$schema": "https://turbo.build/schema.json",
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**", ".next/**"]
    },
    "dev": {
      "cache": false,
      "persistent": true
    },
    "lint": {
      "outputs": []
    },
    "test": {
      "dependsOn": ["build"],
      "outputs": ["coverage/**"]
    }
  }
}

#### 核心特性:智能缓存

bash snippetbash
# 首次构建
$ turbo run build
• build  Completed in 2m30s

# 修改一个包后再次构建
$ turbo run build  
• build  Completed in 45s  (缓存命中)

#### 远程缓存

bash snippetbash
# 登录 Vercel
npx turbo login

# 链接远程缓存
npx turbo link

# 团队共享缓存
# CI/CD 中自动使用远程缓存

#### 并行执行

Turborepo 自动分析依赖图,并行执行独立任务:

code snippetcode
app-a ──┐
        ├───> build (可并行)
app-b ──┘

shared ──> build --> app-a/build (依赖等待)

Nx:企业级方案

Nx 是更完整的 Monorepo 解决方案:

json snippetjson
// nx.json
{
  "targetDefaults": {
    "build": {
      "dependsOn": ["^build"]
    }
  }
}
bash snippetbash
# 安装
npm install -D nx

# 创建应用
npx nx g @nx/react:app web
npx nx g @nx/react:lib ui

# 构建
npx nx build web

# affected 模式:只构建变更的部分
npx nx affected:build

#### Nx 插件生态

bash snippetbash
# React
npm i -D @nx/react

# Next.js
npm i -D @nx/next

# Node.js
npm i -D @nx/node

# Storybook
npm i -D @nx/storybook

#### 计算缓存

bash snippetbash
# 本地缓存
npx nx reset  # 清除缓存

# 分布式缓存(Nx Cloud)
npx nx connect-to-nx-cloud

实际项目结构

code snippetcode
my-monorepo/
├── pnpm-workspace.yaml
├── turbo.json
├── package.json
│
├── apps/
│   ├── web/              # Next.js 主站
│   │   ├── src/
│   │   ├── project.json
│   │   └── package.json
│   ├── admin/            # 管理后台
│   └── mobile/           # React Native
│
├── packages/
│   ├── ui/               # 组件库
│   │   ├── src/
│   │   ├── stories/
│   │   ├── project.json
│   │   └── package.json
│   │
│   ├── shared/           # 共享工具
│   │   ├── src/
│   │   ├── project.json
│   │   └── package.json
│   │
│   ├── config/           # 配置
│   │   ├── eslint/
│   │   ├── tsconfig/
│   │   └── vite/
│   │
│   └── utils/            # 工具函数
│       ├── src/
│       ├── project.json
│       └── package.json
│
└── tools/
    └── scripts/          # 构建脚本

迁移建议

#### 从 Multi-repo 迁移

bash snippetbash
# 1. 创建 Monorepo 仓库
mkdir my-monorepo && cd $_
git init

# 2. 创建 workspace 配置
echo "packages: ['packages/*', 'apps/*']" > pnpm-workspace.yaml

# 3. 迁移现有项目
mv ../project-a apps/
mv ../project-b packages/

# 4. 调整依赖
# 将 package.json 中的依赖改为 workspace 协议

#### 注意事项

  • 权限控制:使用 GitHub Actions 限制应用访问
  • 发布流程:CI/CD 需要处理独立发布
  • 代码所有权: CODEOWNERS 管理包权限

选型对比

特性PNPM WorkspaceTurborepoNx
依赖管理内置依赖其他内置
构建缓存
远程缓存VercelNx Cloud
学习曲线
企业功能
适用小团队中团队大企业

最佳实践

  • 统一代码风格:ESLint + Prettier + Husky
  • 类型共享:typescript 依赖提升到根目录
  • 版本管理:changesets 自动版本控制
  • CI/CD:affected 模式只构建变更
bash snippetbash
# changesets 用法
pnpm add -Dw @changesets/cli
pnpm changeset init

# 发布
pnpm changeset version
pnpm publish -r

总结

Monorepo 是现代前端工程的趋势:

  • 小团队:PNPM Workspace 足够
  • 中团队:PNPM + Turborepo
  • 大企业:Nx 全家桶

核心收益是开发体验维护效率,但也要考虑迁移成本。