⚡ Lerna 与 Turborepo
一句话定性
这是 Monorepo 工具的两代接力。Lerna(2015) 是第一代——解决”一个仓里那么多包,版本怎么管、怎么批量发布”;但它没碰 Monorepo 真正的魔王:构建放大,后来维护停滞、一度被宣告无人维护,最终被 Nrwl 接手续命。Turborepo(2021,Vercel,Rust) 直击要害——用任务编排 + 增量构建 + remote cache,让”改一个包要重建所有”变成”只重建受影响的、其余命中缓存”,而且缓存能全团队共享。
前置:为什么需要这类工具
Monorepo 的根本痛点是 构建放大(build amplification):改一个底层包,理论上要重建所有依赖它的包,CI 时间随仓库爆炸。包管理器的
workspaces只解决”装在一起”,不解决”怎么高效构建”。背景见 Monorepo与workspaces。
一、它是什么 & 出现的时代
| 工具 | 出现 | 出身 | 核心定位 |
|---|---|---|---|
| Lerna | 2015 | 社区(Babel 团队为管理自身 Monorepo 而创) | 第一代:版本管理 + 批量发布 |
| Turborepo | 2021 | Vercel(2021 收购,作者 Jared Palmer) | 第二代:增量构建 + remote cache,Rust 编写 |
- Lerna 诞生于 2013-2018 SPA时代 末:Babel 这种本身就是 Monorepo 的项目,急需一个工具批量管理几十个子包的版本与发布。
- Turborepo 诞生于 2018-2023 工程化时代 的 Rust 工具链浪潮中,与 Turbopack 同属 Vercel 的 “Turbo” 系(共享底层 Rust 增量引擎血统)。
二、为什么会出现(解决上一代什么痛点)
Lerna 解决的:版本与发布的混乱
Monorepo 早期的发版地狱
一个仓里 30 个包,互相依赖。发版时:哪些包变了?变了的包,依赖它的包要不要也跟着升 patch?版本号谁来 bump?要逐个
npm publish30 次吗?Lerna 把这套苦力自动化:
lerna version:检测哪些包有改动,自动 bump 版本、更新互相依赖的版本号、打 tag。lerna publish:把变更的包批量发布到 npm-registry与包生态。- 两种模式:fixed(所有包统一版本号,如 Babel)/ independent(每个包独立版本)。
Turborepo 解决的:Lerna 没碰的”构建放大”
Lerna 的致命盲区
Lerna 擅长版本与发布,但对 Monorepo 真正的性能魔王——构建放大——几乎无能为力。它能按拓扑顺序跑任务,却不知道”这次改动到底影响了哪些包”,更没有缓存。结果:改一行,CI 还是把全仓重新 build + test 一遍。仓库越大越痛。
Turborepo 的出发点就一句话:让构建的代价只与”改动的影响范围”成正比,而不是与”仓库总大小”成正比。
三、核心机制 & 为什么流行
Lerna 的机制(第一代)
lerna changed ──► 检测哪些包自上次 tag 后有改动
▼
lerna version ──► bump 版本 + 更新内部依赖引用 + 打 git tag
▼
lerna publish ──► 按依赖拓扑顺序批量 publish 到 registry
本质是版本/发布的批处理器,价值在”管理与分发”,不在”构建性能”。
Turborepo 的机制(第二代,直击构建放大)
① 任务编排 + 依赖图
Turborepo 读 turbo.json 里声明的任务依赖(如 build 依赖上游包的 build),构建出一张任务依赖图,据此最大化并行、按正确顺序执行。
② 增量 + 内容寻址缓存(核心)
对每个任务(如某个包的 build):
① 收集所有输入:源码 + 依赖 + 配置 + 环境 ──► 算一个 hash
▼
② 查缓存:这个 hash 之前构建过吗?
├── 命中 ──► 直接还原产物 + 日志(几毫秒,跳过构建)
└── 未命中 ──► 真正执行,产物连同 hash 存入缓存
▼
③ 只有【输入 hash 变了】的任务才真正重跑
└──► 重算量 ∝ 改动影响范围,而非仓库大小
③ Remote Cache(团队共享缓存) — Turborepo 最杀手锏的一招:
remote cache:把"别人构建过的结果"借来用
本地缓存只对你自己有效。Remote cache 把构建产物上传到团队共享的远端(Vercel 托管或自建)。于是:
- 同事拉了你的分支,他改动之外的包直接下载你构建好的产物,无需本地重建。
- CI 命中缓存:绝大多数包从未变过,CI 直接复用历史产物,全仓 CI 从几十分钟降到几分钟。
这是”构建一次,全团队 + 全 CI 复用”——把增量缓存的收益从”单机”放大到”组织”。
为什么流行:轻量(几乎零配置就能接管已有 Monorepo)+ 直击痛点(构建放大)+ Vercel 渠道(与 Next.js 生态天然亲和)+ remote cache 的即时收益肉眼可见。
与 Turbopack 不要混淆
同属 Vercel “Turbo” 系、都讲”增量”,但层次不同:
- Turborepo = Monorepo 任务编排器(调度 build/test/lint 等任务 + 缓存任务结果)。
- Turbopack = 打包器(替代 Webpack,做模块级增量编译)。 一个管”任务级”,一个管”模块级”。
四、带来的新问题 / 副作用
两代各自的坑
Lerna:
- 不解决构建放大,大型 Monorepo 下性能跟不上 ──► 这是它被边缘化的根因。
- 后期与 Yarn/pnpm 原生 workspaces 功能重叠,定位尴尬(版本/发布之外能力被包管理器和 Turbo/Nx 蚕食)。
Turborepo:
- 缓存正确性是双刃剑:hash 的输入必须完整——漏算了某个环境变量或隐式输入,就会命中”脏缓存”,拿到错误产物,且极难排查。
- 只做编排与缓存,不做”全家桶”:刻意保持轻量,不提供代码生成、依赖图可视化、affected 等重能力(那是 Nx 的地盘)——是优点也是局限。
- remote cache 的信任与安全:共享产物意味着要信任缓存源,自建 remote cache 有运维成本。
五、为什么会衰落 / 现状
Lerna:停滞 → 被 Nrwl 接手
Lerna 的兴衰
Lerna 长期是 Monorepo 的代名词,但 2020 年前后核心维护陷入停滞,官方一度挂出”项目处于维护模式 / 未在积极维护”的声明,社区一片唱衰。2022 年,Nrwl(Nx 的母公司)接手了 Lerna,给它续命并集成进 Nx 的能力(Lerna 现在可借 Nx 的任务缓存)。如今 Lerna 仍存活,但已不再是新项目的默认选择——它的”版本/发布”价值还在,“构建编排”则交给了 Turbo/Nx。
Turborepo:当红,但赛道在卷
现状(2026)
六、对后续技术的影响(因果链)
[[Monorepo与workspaces]]:多包同仓 ──► 制造【构建放大】魔王
│
├──► Lerna(2015):版本管理 + 批量发布
│ │ 价值在"管理/分发",不碰构建放大
│ ▼
│ 维护停滞(~2020)──► 唱衰 ──► Nrwl 接手续命(2022,接入 Nx 缓存)
│
└──► Turborepo(2021,Vercel,Rust):直击构建放大
│ ① 任务依赖图 ──► 最大化并行
│ ② 内容寻址缓存 ──► 只重建输入变了的任务
│ ③ remote cache ──► 构建一次,全团队 + CI 复用
▼
Monorepo 编排共识固化:增量 + 缓存 + 依赖图
│
├──► [[Nx]]:把这套做成企业级"全家桶"(affected + 代码生成 + 插件)
│ ▲ 重型参照:Bazel(Google,远程缓存/封闭构建的鼻祖)
│
└──► 与 [[Turbopack]] / [[Vite]] 同属 Rust 工具链大潮
历史地位
Lerna 与 Turborepo 的交接,是一次清晰的”问题升级”叙事:第一代(Lerna)解决的是 Monorepo 的管理问题(版本/发布),第二代(Turborepo)解决的是 Monorepo 的性能问题(构建放大)。当 Monorepo 规模从”几十个包”涨到”几百个包”,痛点从”发版麻烦”变成”CI 跑不动”,工具的核心命题也随之从编排转向缓存。Turborepo 的 remote cache 更点破了一个朴素而深刻的事实:在共享代码的世界里,连”构建结果”本身都值得被共享。
🔗 相关:包生态与Monorepo演进史 | Monorepo与workspaces | Nx | npm-registry与包生态 | npm-Yarn-pnpm-包管理 | Turbopack | Vite | 2013-2018 SPA时代 | 2018-2023 工程化时代