2025-10-17-【工程化】Monorepo(单仓库多项目)工具对比

前言

在 Monorepo(单仓库多项目)管理领域,Lerna 是曾经的“老大哥”,而 Turborepo 和 Nx 则是目前最主流的、追求“极速”体验的新一代构建工具。

以下是它们在核心逻辑、性能和适用场景上的深度对比。


1. 核心定位差异

工具 定位 核心哲学
Lerna 包发布/版本管理 早期定位是管理 packages 的依赖链接和发布流程,本身不擅长构建加速。
Turborepo 构建加速器 追求“零配置”和极致的缓存(Cache)。它是基于任务(Task)的,能极大地缩短重复构建时间。
Nx 全栈管理框架 功能最全,提供代码生成器、项目依赖分析图谱,适合超大型企业级项目的精细化管理。

2. 关键特性对比

Lerna:经典但已“换芯”

Lerna 曾经因为维护停滞几乎被放弃,但后来被 Nx 的母公司 Nrwl 接管并进行了现代化重构。

  • 优势:发布(Publishing)流程非常成熟。它能自动处理版本号更新、生成 Changelog。
  • 现状:现在的 Lerna 15+ 内部其实集成了 Nx 的部分引擎。如果你只需要管理版本并发布到 npm,Lerna 依然是直观的选择。

Turborepo:简单且快(Vercel 出品)

Turbo 的核心是 “不重复做已经做过的工作”

  • 远程缓存 (Remote Caching):如果你在加拿大节点构建了一次,你的同事在本地构建时可以直接拉取你的缓存结果,无需重新编译。
  • 任务编排:通过简单的 turbo.json 定义任务依赖(比如:只有当依赖包 build 完了,App 才能 build)。
  • 语言优势:用 Rust 编写,启动速度极快。

Nx:强大的生态图谱

Nx 是目前 Monorepo 界的“天花板”,它不仅是构建工具,更是开发标准。

  • 依赖分析 (Graph):运行 nx graph 可以可视化看到项目中所有包的调用关系。
  • 受影响测试 (Affected Commands):如果你只改了 A 包,Nx 会智能判断出哪些包依赖 A,并只运行相关的测试和构建,避免全量运行。
  • 计算缓存:同样支持本地和云端缓存。

3. 性能深度对比

特性 Lerna (传统版) Turborepo Nx
执行方式 串行或简单并行 并行计算 + 任务编排 智能任务编排
本地缓存 无(除非结合 Nx)
增量构建
配置复杂度 低 (JSON) 中/高

4. 底层实现原理

从底层实现来看,这三者的区别本质上是**“脚本执行器”“计算引擎”**之间的代差。Lerna 代表了早期的拓扑排序逻辑,而 Turbo 和 Nx 则引入了现代编译器中的“控制流图”与“内容寻址缓存”概念。


4.1 Lerna:基于拓扑排序的串行/并行执行器

Lerna 的传统底层逻辑非常简单,它主要解决的是 Package 间的依赖顺序

  • 依赖图构建:读取每个 package.json 中的 dependencies,构建一个 DAG(有向无环图)
  • 拓扑排序(Topological Sort):根据图的结构确定执行顺序。例如 B 依赖 A,那么 Lerna 会先执行 A 的 build,完成后再启动 B。
  • 执行瓶颈:传统的 Lerna 不具备“任务级”细粒度控制。即使你只改了 A 包的一个 README,Lerna 依然会重新执行 A 的完整构建逻辑,因为其底层没有 Input/Output 状态校验机制

4.2 Turborepo:基于内容的哈希(Content-Aware Hashing)

Turborepo 的底层是由 Rust 编写的,它的核心是 指纹追踪(Fingerprinting)

  • 哈希计算(Hashing):在执行任务前,Turbo 会根据以下四个维度计算一个唯一的哈希值:
  1. 源码文件的内容(仅限该任务关联的文件)。
  2. 依赖包的哈希值。
  3. 环境变量(如 NODE_ENV)。
  4. 命令行参数。
  • 干预执行(The “Dry Run”):如果计算出的哈希值在本地或远程缓存中已存在,Turbo 直接跳过执行,将缓存的 dist 目录和日志直接“搬”到对应位置。
  • 任务编排(Task Pipeline):它不以 Package 为最小单位,而是以 Task 为单位。比如,所有包的 lint 任务可以跨包并行,而不必等待某个包完整 build 结束。

4.3 Nx:基于受影响分析与计算缓存(Computation Caching)

Nx 的底层比 Turbo 复杂得多,它更像是一个静态代码分析器

  • 静态分析(Static Analysis):Nx 不仅仅看 package.json,它会扫描源代码中的 importrequire 语句,构建一个比 Lerna 更精确的源码级依赖图
  • Affected 算法:通过 git diff 比对改动的文件,逆向推导出受影响的节点。
  • 执行图解耦:Nx 将“做什么”(Task)和“怎么做”(Executor)解耦。它的计算缓存不仅缓存文件,还缓存内存状态
  • 分布式执行(DTE):这是 Nx 的杀手锏。它能将任务图拆分,分发到多台机器并行执行,并处理它们之间的产物依赖,这在 2 核 1G 的小机器上可能感知不强,但在大型 CI 集群中是质变。

4.4 底层差异深度对比表

特性 Lerna (Legacy) Turborepo Nx
底层语言 JavaScript Rust (核心引擎) TypeScript / Rust
缓存颗粒度 无 (默认) 任务级文件缓存 源码级依赖 + 任务缓存
依赖识别 仅依赖 package.json 仅依赖 package.json 源码 AST 分析
执行模型 拓扑排序 (串行/简单并行) 任务管线并行 (Pipeline) 高级受影响分析并行
产物重用 重新生成 文件系统软链接/拷贝 文件系统镜像

如何选择

  • 选择 Turborepo 的场景:如果你追求轻量、快、配置简单。对于 1G 内存的服务器,Turbo 的开销极小,且它的并行构建和缓存机制能显著降低构建时的 CPU 压力。
  • 选择 Nx 的场景:如果你的项目非常庞大,包含几十个子项目,且你需要严格的架构约束(比如禁止 A 包引用 B 包),或者需要强大的 IDE 插件支持。
  • 选择 Lerna 的场景:如果你只是开发几个 npm 插件,主要痛点在于如何方便地更新版本号和发布,且对构建速度没有极高要求。
  • Turborepo 的优势:由于底层是 Rust,它的内存开销极小。在 1G 内存的机器上,Turbo 扫描数千个文件计算哈希的速度极快,不会导致 Node.js 进程因为内存压力频繁 GC。
  • Nx 的考量:Nx 的静态分析插件(尤其是包含大量代码生成器时)对内存有一定要求。如果你的 Monorepo 非常大,在 1G 内存机器上跑 nx graph 可能会比较吃力。
  • 缓存的作用:对于你的境外节点,开启 Remote Cache。这样你在本地 M1 Mac 上构建好的 dist 产物,在 Debian 服务器上部署时,底层引擎发现哈希一致,会直接从云端下载结果,而不需要在弱鸡的服务器 CPU 上进行编译


2025-10-17-【工程化】Monorepo(单仓库多项目)工具对比
https://zhangyingxuan.github.io/2025-10-17-【工程化】Monorepo(单仓库多项目)工具对比/
作者
blowsysun
许可协议