⚡ Lerna 与 Turborepo

一句话定性

这是 Monorepo 工具的两代接力。Lerna(2015) 是第一代——解决”一个仓里那么多包,版本怎么管、怎么批量发布”;但它没碰 Monorepo 真正的魔王:构建放大,后来维护停滞、一度被宣告无人维护,最终被 Nrwl 接手续命。Turborepo(2021,Vercel,Rust) 直击要害——用任务编排 + 增量构建 + remote cache,让”改一个包要重建所有”变成”只重建受影响的、其余命中缓存”,而且缓存能全团队共享

前置:为什么需要这类工具

Monorepo 的根本痛点是 构建放大(build amplification):改一个底层包,理论上要重建所有依赖它的包,CI 时间随仓库爆炸。包管理器的 workspaces 只解决”装在一起”,不解决”怎么高效构建”。背景见 Monorepo与workspaces


一、它是什么 & 出现的时代

工具出现出身核心定位
Lerna2015社区(Babel 团队为管理自身 Monorepo 而创)第一代:版本管理 + 批量发布
Turborepo2021Vercel(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 publish 30 次吗?

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)

  • Turborepo 是轻量 Monorepo 编排的当红主流,尤其在 Next.js / Vercel 生态。
  • 取舍光谱:轻量缓存编排(Turborepo)⟷ 重型企业平台(Nx);两者都把”增量 + 缓存 + 依赖图”作为共识基石,区别在功能广度与约束强度。
  • 底层潮流:与 TurbopackVite 的 Rolldown 同属”Rust 重写工具链”大潮——Monorepo 编排层也完成了向系统级语言的迁移。

六、对后续技术的影响(因果链)

[[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 工程化时代