本文是《从零到一:构建现代化企业级 Monorepo 项目实战》系列的第二篇。上一篇我们深入分析了 Monorepo 的核心概念,这篇文章将聚焦于工具选型,带你了解主流 Monorepo 工具的优劣,以及我的选择理由。
🎯 本文目标
读完这篇文章,你将了解:
- 主流 Monorepo 工具的对比分析
- pnpm workspace 的核心优势
- Turborepo 为什么这么快
- 如何根据项目规模选择合适的工具
📖 Monorepo 工具全景图
工具分类
scss
Monorepo 工具链
├── 包管理器层
│ ├── npm workspaces
│ ├── yarn workspaces
│ └── pnpm workspace ⭐ (我的选择)
│
├── 构建编排层
│ ├── Lerna
│ ├── Rush
│ ├── Nx
│ └── Turborepo ⭐ (我的选择)
│
└── 一体化方案
├── Nx (包管理 + 构建)
└── Rush (包管理 + 构建)
🔍 包管理器对比
npm workspaces vs yarn workspaces vs pnpm workspace
特性 | npm | yarn | pnpm | 推荐指数 |
---|---|---|---|---|
安装速度 | ⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | 🏆 pnpm |
磁盘空间 | ⭐⭐ | ⭐⭐ | ⭐⭐⭐⭐⭐ | 🏆 pnpm |
依赖隔离 | ⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐⭐⭐ | 🏆 pnpm |
幽灵依赖 | ❌ 有 | ❌ 有 | ✅ 无 | 🏆 pnpm |
生态成熟度 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | 🏆 npm |
学习成本 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | 🏆 npm |
pnpm 的杀手级特性
1. 节省磁盘空间(最多节省 75%)
传统 npm/yarn:
bash
# 每个项目都复制一份依赖
~/projects/
├── project-a/node_modules/lodash/ # 1MB
├── project-b/node_modules/lodash/ # 1MB
├── project-c/node_modules/lodash/ # 1MB
└── project-d/node_modules/lodash/ # 1MB
# 总共 4MB
pnpm 的硬链接:
bash
# 所有项目共享同一份依赖
~/.pnpm-store/
└── lodash@4.17.21/ # 1MB(只存一份)
~/projects/
├── project-a/node_modules/lodash/ → 硬链接
├── project-b/node_modules/lodash/ → 硬链接
├── project-c/node_modules/lodash/ → 硬链接
└── project-d/node_modules/lodash/ → 硬链接
# 总共只占用 1MB!
实际效果:
bash
# 我的项目数据
npm: 1.2 GB node_modules
pnpm: 350 MB node_modules
# 节省空间:70.8%!
2. 杜绝幽灵依赖
什么是幽灵依赖?
typescript
// package.json 中没有声明 lodash
{
"dependencies": {
"some-package": "^1.0.0" // some-package 依赖了 lodash
}
}
// 但你居然可以直接用!这就是幽灵依赖
import _ from 'lodash' // 😱 能用,但不安全!
pnpm 的严格模式:
bash
# pnpm 会报错
Error: Cannot find module 'lodash'
# 必须显式声明依赖才能使用 ✅
3. 更快的安装速度
性能对比(安装 1000+ 依赖):
makefile
npm: 45s ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
yarn: 32s ▓▓▓▓▓▓▓▓▓▓▓▓
pnpm: 12s ▓▓▓▓ ⚡
速度提升:
- 比 npm 快 3.75 倍
- 比 yarn 快 2.67 倍
🚀 构建编排工具对比
Lerna vs Rush vs Nx vs Turborepo
📊 综合对比
工具 | 学习曲线 | 性能 | 功能丰富度 | 配置复杂度 | 社区活跃度 | 推荐指数 |
---|---|---|---|---|---|---|
Lerna | ⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐ |
Rush | ⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐ | ⭐⭐⭐ |
Nx | ⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
Turborepo | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
1️⃣ Lerna:老牌工具,渐显疲态
优势:
bash
# 简单易用
lerna init
lerna bootstrap
lerna publish
劣势:
- ❌ 性能较差(没有缓存机制)
- ❌ 功能有限(主要是版本管理)
- ❌ 维护不活跃(已转交给 Nx 团队)
适用场景: 小型项目,简单的版本管理需求
2️⃣ Rush:微软出品,企业级方案
优势:
json
// rush.json - 强大的配置能力
{
"projects": [
{ "packageName": "ui-lib", "projectFolder": "packages/ui" }
],
"pnpmOptions": {
"strictPeerDependencies": true
}
}
特点:
- ✅ 严格的依赖管理
- ✅ 企业级特性完善
- ✅ 支持 pnpm
劣势:
- ❌ 学习曲线陡峭
- ❌ 配置复杂
- ❌ 社区相对小众
适用场景: 大型企业项目,需要严格管理
3️⃣ Nx:功能最强大的方案
优势:
bash
# 强大的代码生成
nx generate @nx/react:component Button
# 智能的依赖图分析
nx graph
# 高效的缓存
nx run-many --target=build --all
特点:
- ✅ 功能最丰富(代码生成、依赖图、插件系统)
- ✅ 性能优秀(智能缓存)
- ✅ 支持多种框架(React、Vue、Angular)
劣势:
- ❌ 学习成本高
- ❌ 配置复杂
- ❌ 上手门槛高
适用场景: 大型项目,需要完整的工具链支持
4️⃣ Turborepo:我的最终选择 🏆
核心优势:
📈 极致的性能
bash
# 真实项目数据对比
无缓存 有缓存 提升倍数
Lerna: 45s 45s 1x
Rush: 38s 12s 3.2x
Nx: 35s 2.5s 14x
Turborepo: 9s 0.45s 20x ⚡
# Turborepo 在缓存命中时快了 19-20 倍!
🎯 极简的配置
Turborepo 配置:
json
// turbo.json - 仅需 76 行配置
{
"$schema": "https://turbo.build/schema.json",
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
}
}
}
对比 Nx 配置:
json
// nx.json + workspace.json + project.json
// 需要 200+ 行配置
🔥 零配置开箱即用
bash
# 3 步搞定
npx create-turbo@latest
cd my-turborepo
pnpm install
# 就这么简单!
⚡ 智能缓存机制
bash
# 第一次构建
pnpm build
✓ @company/utils built in 2.1s
✓ @company/ui built in 3.4s
# 代码没变化,再次构建
pnpm build
✓ @company/utils cached ⚡
✓ @company/ui cached ⚡
# 完成时间:0.3s!
🎨 我的技术选型过程
项目需求分析
vbnet
GDU Common 项目特点:
✓ 4 个包(ui、utils、shared、controls-sdk)
✓ 都使用 Vue 3 + TypeScript
✓ 需要频繁联调
✓ 团队 5-8 人
✓ 需要快速迭代
决策树
开始
↓
需要 Monorepo? → 是
↓
团队规模? → 5-8人(中小型)
↓
是否需要代码生成? → 否
↓
是否需要多框架支持? → 否(只用 Vue)
↓
最看重什么? → 性能 + 简单
↓
选择:pnpm + Turborepo ✅
选型理由
1️⃣ pnpm workspace
为什么不选 npm/yarn?
bash
# npm workspaces 的问题
npm install
# 幽灵依赖问题
# 速度较慢
# yarn workspaces 的问题
yarn install
# 依赖提升导致的版本冲突
# PnP 模式不够成熟
# pnpm workspace 的优势
pnpm install
# ✅ 快速
# ✅ 严格
# ✅ 节省空间
pnpm-workspace.yaml 配置:
yaml
packages:
- packages/*
- docs
- build
就这么简单!
2️⃣ Turborepo
为什么不选 Nx?
bash
# Nx 的问题
- 配置复杂(3-4 个配置文件)
- 学习曲线陡
- 功能过于丰富(我们用不上)
# Turborepo 的优势
- 配置简单(1 个 turbo.json)
- 性能极致(Go 语言编写)
- 专注于构建(做好一件事)
为什么不选 Lerna?
bash
# Lerna 的问题
- 性能差(无缓存机制)
- 功能有限
- 维护不活跃
# 数据对比
Lerna: 45s 构建
Turborepo: 9s 构建(无缓存)
0.45s 构建(有缓存)
# 差距太明显了!
🛠️ pnpm 深度解析
核心原理:基于符号链接的依赖管理
传统的 node_modules 结构(npm/yarn)
perl
node_modules/
├── package-a/
│ ├── index.js
│ └── node_modules/
│ └── package-b/ # 依赖被提升到顶层
├── package-b/ # 重复了!
└── package-c/
问题:
- 依赖提升导致幽灵依赖
- 重复的依赖占用空间
pnpm 的 content-addressable 存储
perl
node_modules/
├── .pnpm/
│ ├── package-a@1.0.0/
│ │ └── node_modules/
│ │ ├── package-a/ → ~//.pnpm-store/...
│ │ └── package-b/ → .pnpm/package-b@1.0.0/...
│ └── package-b@1.0.0/
│ └── node_modules/
│ └── package-b/ → ~/.pnpm-store/...
└── package-a/ → .pnpm/package-a@1.0.0/...
优势:
- ✅ 扁平的 node_modules,但严格的依赖隔离
- ✅ 全局存储,硬链接复用
- ✅ 避免幽灵依赖
实战配置
package.json:
json
{
"name": "gdu-common",
"private": true,
"scripts": {
"install": "pnpm install"
}
}
pnpm-workspace.yaml:
yaml
packages:
- packages/*
- docs
- build
.npmrc 配置:
ini
# 使用严格的 peer 依赖检查
strict-peer-dependencies=true
# 不要幽灵依赖
shamefully-hoist=false
# 使用硬链接
link-workspace-packages=true
常用命令
bash
# 安装依赖
pnpm install
# 添加依赖到根目录
pnpm add -w lodash-es
# 添加依赖到特定包
pnpm add vue --filter @gdu-common/ui
# 运行所有包的脚本
pnpm -r build
# 只运行特定包
pnpm --filter @gdu-common/ui build
⚡ Turborepo 深度解析
核心概念
1. 任务编排(Task Orchestration)
json
// turbo.json
{
"tasks": {
"build": {
"dependsOn": ["^build"], // ^ 表示依赖包的 build
"outputs": ["dist/**"]
},
"test": {
"dependsOn": ["build"], // 先 build 再 test
"outputs": ["coverage/**"]
}
}
}
依赖图:
less
@gdu-common/ui:build
↓ 依赖
@gdu-common/utils:build
↓ 依赖
@gdu-common/shared:build
# Turborepo 会自动计算正确的执行顺序
2. 智能缓存(Smart Caching)
缓存键计算:
bash
# Turborepo 会基于这些内容计算缓存键
- 源代码的哈希值
- 依赖的哈希值
- 环境变量
- 任务配置
# 任何一个变化,缓存失效
缓存命中示例:
bash
$ pnpm build
Tasks: 4 successful, 4 total
Cached: 4 cached, 4 total ⚡
Time: 450ms >>> FULL TURBO
# 4 个包全部命中缓存,只用了 450ms!
3. 并行执行(Parallel Execution)
bash
# Turborepo 自动分析依赖关系,最大化并行
时间轴 →
shared:build ▓▓▓
↓
utils:build ▓▓▓▓
↓
ui:build ▓▓▓▓▓
docs:build ▓▓▓▓▓▓▓▓
# shared 和 docs 可以并行
# utils 等待 shared 完成
# ui 等待 utils 完成
真实性能数据
我的项目构建性能:
场景 | 时间 | 缓存命中 | 说明 |
---|---|---|---|
首次构建 | 9.2s | 0/4 | 无缓存 |
完全缓存 | 450ms | 4/4 ⚡ | FULL TURBO |
修改 1 个包 | 2.3s | 3/4 | 增量构建 |
修改配置文件 | 9.1s | 0/4 | 配置变化,缓存失效 |
效率提升:
- 完全缓存时提升 20.4 倍 🚀
- 日常开发平均提升 4-5 倍
Turborepo 配置实战
基础配置:
json
{
"$schema": "https://turbo.build/schema.json",
"ui": "tui", // 使用终端 UI
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".vitepress/dist/**"],
"cache": true
},
"lint": {
"cache": true
},
"lint:fix": {
"cache": false // 修改文件的任务不缓存
},
"clean": {
"cache": false
}
}
}
高级配置:
json
{
"tasks": {
"build": {
"dependsOn": ["^build"],
"inputs": [
"$TURBO_DEFAULT$",
"!{dist,build,coverage,.turbo}/**",
"!**/*.md",
"!**/*.test.{ts,tsx}"
],
"outputs": ["dist/**"],
"cache": true
}
},
"globalDependencies": [
".env",
"tsconfig.json",
"eslint.config.js"
],
"globalEnv": ["NODE_ENV", "CI"]
}
🎯 最终选择:pnpm + Turborepo
组合优势
markdown
pnpm workspace (包管理)
+
Turborepo (构建编排)
=
完美组合 🎉
1. pnpm 负责依赖管理
- 快速安装
- 节省空间
- 严格隔离
2. Turborepo 负责构建编排
- 智能缓存
- 并行执行
- 增量构建
3. 强强联合
bash
# pnpm 快速安装依赖
pnpm install # 12s
# Turborepo 快速构建
pnpm build # 9s(首次) / 0.45s(缓存)
# 总时间:13s(首次) / 12.5s(缓存)
实际效果
开发效率提升:
bash
# 传统 Multirepo 工作流
修改共享函数 → 发布 → 更新依赖 → 重新安装 → 测试
总耗时:5-10 分钟 😫
# Monorepo + Turborepo 工作流
修改共享函数 → 保存 → 自动重建 → 热更新
总耗时:2-3 秒 ⚡
# 效率提升:100-200 倍!
CI/CD 性能:
yaml
# .gitlab-ci.yml
build:
script:
- pnpm install # 3s
- pnpm build # 9s (首次)
# 后续 pipeline 只需 0.5s!
💡 选型建议
根据项目规模选择
小型项目(2-3 个包)
bash
✅ 推荐:pnpm workspace
❌ 不需要:Turborepo
# 理由:包少,构建快,不需要复杂的编排
中型项目(4-10 个包)
bash
✅ 推荐:pnpm + Turborepo
⭐ 最佳组合!
# 理由:缓存和并行构建带来明显收益
大型项目(10+ 个包)
bash
✅ 推荐:pnpm + Turborepo
或
✅ 推荐:pnpm + Nx
# Nx 提供更多功能(代码生成、依赖图)
# Turborepo 更简单,性能更好
# 根据团队技术储备选择
根据团队特点选择
团队特点 | 推荐方案 |
---|---|
前端团队,技术栈统一 | pnpm + Turborepo |
全栈团队,多技术栈 | pnpm + Nx |
大型企业,严格管理 | pnpm + Rush |
简单项目,快速上手 | pnpm workspace |
🚀 快速体验
创建一个 Turborepo 项目
bash
# 使用官方脚手架
npx create-turbo@latest my-monorepo
# 选择 pnpm
? Which package manager do you want to use? › pnpm
# 项目结构
my-monorepo/
├── apps/
│ ├── web/
│ └── docs/
├── packages/
│ ├── ui/
│ └── eslint-config/
├── turbo.json
└── package.json
运行命令
bash
# 安装依赖
pnpm install
# 构建所有包
pnpm build
# 查看缓存效果
pnpm build # 第二次运行,体验闪电般的速度 ⚡
📊 成本收益分析
迁移成本
项目 | 学习成本 | 迁移时间 | 配置复杂度 |
---|---|---|---|
Lerna | 1 天 | 2-3 天 | 低 |
Rush | 3-5 天 | 1-2 周 | 高 |
Nx | 5-7 天 | 1-2 周 | 高 |
Turborepo | 半天 | 1-2 天 | 低 |
长期收益
开发效率:
- 跨包重构时间减少 80%
- 本地构建时间减少 90%(缓存命中)
- CI/CD 时间减少 70%
维护成本:
- 配置文件减少 75%(统一管理)
- 依赖冲突减少 90%
- 版本管理复杂度降低 80%
团队协作:
- 代码审查效率提升 50%
- 跨项目问题定位快 3 倍
- 新人上手时间减少 60%
🎉 总结
经过详细的对比和实践,我选择了 pnpm + Turborepo 组合,理由是:
pnpm 的三大优势
- ✅ 快 - 安装速度比 npm 快 3.75 倍
- ✅ 省 - 节省 70% 磁盘空间
- ✅ 严 - 杜绝幽灵依赖,依赖管理更安全
Turborepo 的三大优势
- ⚡ 极致性能 - 缓存命中时快 20 倍
- 🎯 极简配置 - 一个 turbo.json 搞定
- 🚀 零学习成本 - 半天上手,开箱即用
实际收益
- 📈 构建速度提升 20 倍(缓存命中)
- 💾 磁盘空间节省 70%
- ⏱️ 开发效率提升 100 倍(跨包修改)
在下一篇文章中,我将手把手带你从零搭建一个完整的 pnpm + Turborepo 项目,包括:
- 项目初始化
- 包结构设计
- 配置文件详解
- 第一个 Hello World 包
🔗 系列文章
- 📖 上一篇: 2025年了,你还在用传统的多仓库管理吗?Monorepo 架构深度解析
- 📖 下一篇: 《从零搭建:pnpm + Turborepo 项目架构实战》
- 🏠 专栏首页: 从零到一:构建现代化企业级 Monorepo 项目实战
你的项目用的是什么 Monorepo 工具?效果如何?欢迎在评论区分享! 🙏
觉得 pnpm + Turborepo 组合不错?点个赞收藏一下,下篇文章将实战搭建! 👍