lerna 和 turbo 都是用于管理 JavaScript 和 TypeScript 多包仓库(Monorepo)的核心工具,旨在解决代码共享、版本控制和构建效率问题。lerna 是这一领域的先驱,主要专注于多包项目的版本发布、变更检测以及依赖链接,它通过运行命令来协调各个包的生命周期。turbo(Turbo Repo)则是一个高性能的构建系统,它引入了基于缓存的任务管道(Pipeline),能够智能地跳过未受影响的任务,并在多核 CPU 上并行执行构建、测试和 lint 操作,极大地提升了大型项目的开发体验。
在大型前端工程中,Monorepo(多包仓库)已成为管理共享代码、统一工具链的标准模式。lerna 和 turbo 是这一领域最知名的两个工具,但它们的侧重点截然不同。简单来说,lerna 更像是一个项目管理者,专注于包的版本和发布;而 turbo 更像是一个高性能引擎,专注于构建速度和任务调度。让我们深入对比它们在真实工程场景中的表现。
lerna 的核心使命是解决“多包如何一起版本化”的问题。
lerna run build),但其执行模型相对线性。# lerna: 典型的发布工作流
# 自动检测变更,提示版本号,更新 package.json 并打标签
lerna version
# 将所有包发布到 npm
lerna publish
turbo 的核心使命是解决“构建太慢”的问题。
# turbo: 典型的构建工作流
# 自动识别依赖关系,并行构建,命中缓存的任务直接跳过
npx turbo run build
# 查看缓存命中情况和任务拓扑图
npx turbo run build --graph
lerna 在运行任务时,默认通常是按顺序或简单的并行处理。
--parallel 标志,但它缺乏对任务间依赖关系的深度理解。// lerna.json 配置示例
{
"packages": ["packages/*"],
"version": "independent",
"command": {
"run": {
"parallel": true
}
}
}
# lerna 运行命令:尝试并行,但缺乏智能依赖排序
lerna run test --parallel
turbo 内置了强大的依赖感知引擎。
turbo.json 中的管道定义,自动构建任务依赖图。// turbo.json 配置示例
{
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
},
"test": {
"dependsOn": ["build"],
"outputs": []
}
}
}
# turbo 运行命令:自动根据依赖图并行执行
# 如果包 A 依赖包 B,turbo 会先构建 B,再构建 A
npx turbo run build
lerna 本身没有内置缓存机制。
lerna run build,它都会重新执行所有脚本。# lerna: 每次都是全量执行
# 即使没有代码变更,也会重新跑一遍
lerna run build
# 输出:[包 A] 构建中... [包 B] 构建中...
turbo 拥有基于内容的缓存系统。
.turbo 文件夹或远程云端恢复输出结果。# turbo: 智能缓存
# 第一次运行:执行构建并缓存
npx turbo run build
# 第二次运行(无代码变更):
# 输出:• Packages in scope: ...
# • Running build in 2 packages
# • Remote caching disabled
# app-ui:build: cache hit, replaying output 8a3d... (0ms)
lerna 提供了经典的 lerna bootstrap 功能(虽在新版中逐渐被包管理器替代)。
node_modules。npm workspaces 或 pnpm workspaces 替代此功能,仅保留 lerna 的发布能力。# lerna: 传统链接方式
# 将 packages 下的所有包链接到彼此和根目录的 node_modules
lerna bootstrap
# 现代推荐:结合 npm workspaces 使用
# package.json
{
"workspaces": ["packages/*"]
}
# 然后仅用 lerna 做发布
lerna publish
turbo 不处理依赖链接。
npm、yarn、pnpm 或 bun 的 Workspaces 功能处理好了依赖。// turbo 项目中,依赖管理完全交给包管理器
// package.json (根目录)
{
"packageManager": "pnpm@8.0.0",
"workspaces": ["apps/*", "packages/*"]
}
// turbo 只关心如何运行任务
// turbo.json
{
"pipeline": { "build": {} }
}
lerna 的配置相对分散。
lerna.json,但具体命令行为往往依赖各个包的 package.json 脚本。// lerna.json: 复杂的发布配置
{
"version": "independent",
"changelog": {
"preset": "angular"
},
"command": {
"publish": {
"registry": "https://my-private-registry.com"
}
}
}
turbo 采用声明式管道配置。
turbo.json 中,清晰定义输入、输出和依赖。// turbo.json: 清晰的管道定义
{
"pipeline": {
"dev": {
"cache": false,
"persistent": true
},
"lint": {
"dependsOn": ["^lint"]
},
"build": {
"dependsOn": ["^build"],
"outputs": [".next/**", "dist/**"]
}
}
}
尽管侧重点不同,两者在宏观目标上是一致的:
packages/ 或 apps/ 这样的目录结构。package.json 中的名称和依赖关系。// 两者都识别这种标准结构
{
"name": "my-monorepo",
"private": true,
"workspaces": ["packages/*"]
}
# lerna 过滤
lerna run test --scope=package-a
# turbo 过滤
npx turbo run test --filter=package-a
| 特性 | lerna | turbo |
|---|---|---|
| 核心强项 | 🏷️ 版本管理、发布、变更日志 | ⚡ 构建速度、缓存、并行管道 |
| 缓存机制 | ❌ 无内置缓存 | ✅ 本地 + 远程共享缓存 |
| 任务调度 | 🔁 线性或简单并行 | 🧠 智能依赖拓扑排序 |
| 依赖链接 | ✅ 内置 (bootstrap) / 推荐配合 Workspaces | ❌ 依赖外部包管理器 (npm/pnpm) |
| 配置风格 | 📝 lerna.json + 分散脚本 | 🗂️ 声明式 turbo.json 管道 |
| 最佳场景 | 需要复杂版本策略的库开发团队 | 追求极致 CI/CD 速度的应用团队 |
选择 lerna 如果:
你正在维护一个组件库或工具包集合,并且最头疼的问题是“如何同时发布 10 个包并保持版本号同步”。你需要它强大的 version 和 publish 命令,以及生成变更日志的能力。在现代架构中,常见的做法是:使用 pnpm workspaces 管理依赖和链接,仅使用 lerna 管理发布流程。这种组合既利用了 pnpm 的速度,又保留了 lerna 的发布优势。
选择 turbo 如果:
你正在开发一个大型应用系统(包含多个前端应用、共享 UI 库、后端服务),并且最头疼的问题是“每次提交都要等 20 分钟构建”。你需要它的缓存机制来加速本地开发,需要它的并行管道来缩短 CI 时间。对于新项目,turbo + pnpm 是目前业界公认的高性能黄金组合。
终极方案:混合使用 很多成熟团队会同时使用两者(或类似组合):
turbo 处理日常的 build, test, lint(追求速度)。lerna 处理最终的 version, publish(追求规范)。// package.json 脚本示例
{
"scripts": {
"build": "turbo run build",
"test": "turbo run test",
"release": "lerna version && lerna publish"
}
}
最后的话:工具没有绝对的优劣,只有适不适合。如果你的痛点是“慢”,选 turbo;如果你的痛点是“乱”(版本混乱),选 lerna。在现代前端工程中,理解它们的边界并将它们组合使用,往往能发挥最大的效能。
如果你的团队主要痛点在于版本管理和发布流程,特别是需要处理复杂的独立版本(Independent Versioning)或自动生成变更日志,那么 lerna 仍然是首选。它非常适合那些已经拥有成熟构建脚本,仅需一个工具来协调包之间依赖链接和发布节奏的项目。注意:现代实践中,常将 lerna 仅用于发布管理,而配合其他工具处理构建。
如果你的项目面临构建速度慢、CI 耗时过长的问题,或者你需要一个能够自动缓存任务结果、仅构建受代码变更影响部分的系统,那么 turbo 是更优的选择。它适合追求极致开发效率、希望利用远程缓存加速团队协作,且愿意采用基于 package.json 脚本声明式配置管道的新项目。
Lerna is a fast, modern build system for managing and publishing multiple JavaScript/TypeScript packages from the same repository.
Check out our docs site here.