Monorepo 概念与工具

Monorepo 的定义、优缺点及 Lerna、Pnpm Workspace 的使用

问题

什么是 Monorepo?常用的 Monorepo 工具有哪些?如何配置和使用?

解答

什么是 Monorepo

Monorepo(单一代码仓库)是将多个项目放在同一个 Git 仓库中管理的策略。

# Multirepo(多仓库)
├── repo-app/
├── repo-ui-components/
└── repo-utils/

# Monorepo(单仓库)
└── monorepo/
    ├── apps/
    │   └── web/
    └── packages/
        ├── ui-components/
        └── utils/

Monorepo vs Multirepo

特性MonorepoMultirepo
代码共享简单直接需要发布 npm 包
依赖管理统一版本各自管理
原子提交支持不支持
构建复杂度较高较低
仓库体积较大较小

Pnpm Workspace

Pnpm 内置 workspace 支持,是目前最推荐的方案。

# pnpm-workspace.yaml
packages:
  - 'packages/*'
  - 'apps/*'
// package.json(根目录)
{
  "name": "my-monorepo",
  "private": true,
  "scripts": {
    "dev": "pnpm -r run dev",
    "build": "pnpm -r run build",
    "test": "pnpm -r run test"
  }
}
// packages/utils/package.json
{
  "name": "@my/utils",
  "version": "1.0.0",
  "main": "dist/index.js",
  "scripts": {
    "build": "tsc",
    "dev": "tsc --watch"
  }
}
// apps/web/package.json
{
  "name": "@my/web",
  "version": "1.0.0",
  "dependencies": {
    // workspace: 协议引用本地包
    "@my/utils": "workspace:*"
  }
}

常用命令:

# 安装所有依赖
pnpm install

# 给指定包添加依赖
pnpm add lodash --filter @my/web

# 给所有包添加开发依赖
pnpm add typescript -Dw

# 运行指定包的脚本
pnpm --filter @my/web dev

# 运行所有包的脚本
pnpm -r run build

Lerna

Lerna 专注于版本管理和发布流程。

// lerna.json
{
  "version": "independent",
  "npmClient": "pnpm",
  "useWorkspaces": true,
  "command": {
    "publish": {
      "conventionalCommits": true,
      "message": "chore(release): publish"
    }
  }
}
# 初始化
npx lerna init

# 查看变更的包
npx lerna changed

# 版本升级
npx lerna version

# 发布到 npm
npx lerna publish

# 在所有包中执行命令
npx lerna run build

Turborepo

Turborepo 专注于构建优化,提供缓存和并行执行。

// turbo.json
{
  "$schema": "https://turbo.build/schema.json",
  "pipeline": {
    "build": {
      // 依赖其他包的 build 先完成
      "dependsOn": ["^build"],
      // 输出目录,用于缓存
      "outputs": ["dist/**"]
    },
    "dev": {
      "cache": false,
      "persistent": true
    },
    "test": {
      "dependsOn": ["build"],
      "outputs": []
    }
  }
}
# 构建所有包(自动处理依赖顺序)
npx turbo run build

# 并行开发
npx turbo run dev

# 只构建变更的包
npx turbo run build --filter=...[HEAD^]

完整项目结构示例

my-monorepo/
├── pnpm-workspace.yaml
├── package.json
├── turbo.json
├── apps/
│   ├── web/
│   │   ├── package.json
│   │   └── src/
│   └── admin/
│       ├── package.json
│       └── src/
├── packages/
│   ├── ui/
│   │   ├── package.json
│   │   └── src/
│   ├── utils/
│   │   ├── package.json
│   │   └── src/
│   └── config/
│       ├── eslint/
│       └── tsconfig/
└── .github/
    └── workflows/

工具选择建议

┌─────────────────────────────────────────────────┐
│  需求                    推荐工具               │
├─────────────────────────────────────────────────┤
│  依赖管理                pnpm workspace         │
│  版本发布                lerna / changesets     │
│  构建优化                turborepo / nx         │
│  综合方案                pnpm + turbo           │
└─────────────────────────────────────────────────┘

关键点

  • Monorepo 将多个项目放在同一仓库,便于代码共享和统一管理
  • Pnpm Workspace 通过 workspace: 协议实现本地包引用,依赖安装高效
  • Lerna 擅长版本管理和 npm 发布,支持 independent 和 dbpnk 两种版本模式
  • Turborepo 提供增量构建和远程缓存,大幅提升构建速度
  • 推荐组合:Pnpm + Turborepo,兼顾依赖管理和构建性能