Monorepo Turbo实践
🎣

Monorepo Turbo实践

创建时间
May 4, 2025 06:14 AM
Description
Monorepo turborepo使用实践
标签
作者

背景与痛点

👋“大家好,我想在开始前问大家一个问题:
假设你在工作多年后,准备启动一个自己的全栈产品:
有前端管理后台、App、小程序,还有后端服务、后台接口……
如果这些模块你都要自己维护,代码分散在多个仓库里,版本要手动控制,部署也要到处点命令,大家有没有想过一个
更统一、高效的方式
🙋“有没有人曾经为这些事头疼过?”
➡️ “今天我们要聊的就是:Monorepo + Turborepo
有人会说 pnpm 不是也能进行包管理 进行工作空间管理嘛
单纯使用 pnpm 就能实现 Monorepo 项目我们都知道,因为它原生支持 workspace,但是它也依然存在一些缺点,例如:
  1. 任务运行策略:虽然 pnpm 支持工作空间间的依赖管理和一些任务的并行执行,但是它并不那么灵活或高效地处理跨包的构建、测试和发布任务比如增量构建和任务缓存,可以显著提高大型 Monorepo 的开发和持续集成的效率。
  1. 自定义构建和发布流程:pnpm 在自定义构建流程、测试流程和发布流程方面的能力有限。对于需要高度定制化工作流的复杂 Monorepo 项目,可能需要额外的脚本或配置来满足这些需求。
  1. 构建效率:在没有专门的构建缓存机制的情况下,pnpm 对于大型 Monorepo 项目的构建效率可能不如使用了缓存策略的工具。这可能导致在持续集成/持续部署(CI/CD)环境中,构建和部署的时间增长。
  1. 高级任务调度和资源管理:pnpm 本身可能不提供高级的任务调度和资源管理特性,如限制并行任务数目以避免过度占用系统资源,或者根据任务的资源使用情况动态调整优先级。
尽管 pnpm 为 Monorepo 提供了强大的支持,并且在许多场景下都能很好地工作,但在需要复杂的任务运行策略、细粒度的任务控制以及高效的任务执行优化时,可能会遇到一些问题,在这种情况下我们可能就需要通过额外的工具、脚本或工作流来补充或优化 pnpm 的使用。
 

Monorepo 简介 & 价值

Monorepo(Monolithic Repository),即“单体代码仓库”,Monorepo 是一种项目代码管理方式,指单个仓库中管理多个项目,有助于简化代码共享、版本控制、构建和部署等方面的复杂性,并提供更好的可重用性和协作性。Monorepo 提倡了开放、透明、共享的组织文化,这种方法已经被很多大型公司广泛使用,如 Google、Facebook 和 Microsoft 等。

Monorepo 工具的演变过程

🛠️ 早期:手动管理 & 静态打包
  • 最早公司自己手写 shell 脚本串联多个子项目
  • 不支持依赖图构建、缓存等能力
  • 工具匮乏、容易出错,维护成本高
🪜 第一阶段:Lerna(2015 起)
  • 第一个广泛使用的 JS Monorepo 工具
  • 以 lerna bootstrap 管理依赖
  • 适合 library 场景,但构建能力弱,不适合现代大型应用
  • 随着 Yarn Workspaces、pnpm 的发展,Lerna 功能逐渐被取代
🚀 第二阶段:Nx、Rush、Bazel 等现代工具
  • 支持:
    • 任务依赖图构建
    • 分布式缓存
    • 细粒度构建优化
  • Nx:前端友好,支持 React、Angular、Nest,CLI 强大, 还支持flutte、Go
  • Rush:适合大规模企业级场景(微软开发)
  • Bazel:谷歌内部方案,配置复杂但性能强大(如 Google Maps)
⚡ 第三阶段:Turborepo(Vercel 推出,2021)
  • 上手简单,零配置集成 pnpm/Yarn/NPM
  • 强调构建缓存、任务管道(pipeline)机制
  • 默认支持现代前端框架(Next.js、React、TS)
  • 轻量但强大,尤其适合中大型项目和独立开发者

为什么是 Turborepo?

工具
介绍
优点
缺点
Rush
由Microsoft开发,为大型Monorepo项目提供全面的支持。具有完整的生态系统,自动版本管理和并行构建。学习曲线较陡峭,更适用于大型项目。
1、完整生态系统 2、自动版本管理 3、并行构建
1、学习曲线较陡峭 2、对小型项目可能显得过于庞大
Turborepo
旨在提供高度可配置的Monorepo解决方案,支持自定义构建流程和任务。灵活性较高,适用于各种项目。
1、高度可配置 2、支持自定义构建流程
1、相对较新,可能缺乏一些成熟的功能和社区支持
Lerna
专注于管理具有多个包的JavaScript项目,提供了一组工具用于优化Monorepo的管理。支持并行构建,版本管理等。
1、专注于JavaScript项目 2、并行构建 3、版本管理方便
1、学习曲线较陡峭 2、配置不够灵活
Yarn Workspaces
Yarn的一部分,提供了一种Monorepo管理方案,允许多个包共享相同的node_modules。轻量级,易于上手。
1、轻量级 2、易于上手
1、功能相对比较单一
Pnpm Workspaces
Pnpm的一部分,提供了与Yarn Workspaces类似的功能,支持多个包共享依赖。通过符号链接进行高效的依赖管理。
1、高效的依赖管理 2、易于上手
1、功能相对比较单一 2、需要适应符号链接的概念
Yalc
允许在不发布到npm仓库的情况下共享本地包,适用于本地开发和测试。
1、本地包共享方便
1、可能不适用于所有场景,特别是在需要发布到公共npm仓库时
npm Workspaces
npm的一部分,提供了在Monorepo中管理包的功能。与Yarn Workspaces类似,但在某些方面有所不同。
1、与npm集成
1、功能相对简单
Nx
针对Angular项目的工具,提供了一套强大的Monorepo管理功能,包括构建、测试、文档生成等。专注于提高Angular项目的开发效率。
1、针对Angular项目优化的强大功能集
1、针对非Angular项目可能显得过于专业化,学习曲线较陡峭
 
上手简单,零配置集成 pnpm/Yarn/NPM
Turborepo 是一个高性能的构建系统,专为 JavaScript 和 TypeScript 的 Monorepo 项目设计。它提供了一种高效管理和构建项目中多个包的方式,通过缓存先前构建和测试的结果来显著减少重复工作的需要,从而加快开发和持续集成的流程。Turborepo 旨在提高大型 Monorepo 项目的构建效率,特别是在复杂的项目中,它可以处理依赖关系、执行任务、并确保构建的正确性和效率。
  1. 多任务并行处理
  1. 更快的增量构建
  1. 云缓存
  1. 任务管道
  1. 基于约定的配置
 
多任务并行处理
notion image
增量构建
“你是否遇到过,明明只改了一个小组件,却要重头构建几十个模块?那你需要的就是 增量构建!”
增量构建(Incremental Build)是指在项目构建过程中,只重新构建发生变化的部分,而跳过那些没有变更的模块或任务,从而大幅度提升构建效率。
增量构建意味着在构建过程中,只有自上次成功构建以来发生变化的部分才会被重新构建,而未更改的部分则会跳过,直接使用上次构建的结果。
Turborepo 首先分析项目的依赖图,包括识别各个包之间的依赖关系。这是增量构建的基础,确保只有当依赖的包发生变化时,依赖它们的包才会被重新构建。
使用文件指纹(或哈希)技术来确定文件自上次构建以来是否发生了更改。通过比较文件的当前指纹与存储在缓存中的上一次构建指纹,Turborepo 能够快速识别哪些文件需要重新构建。
Turborepo 利用先进的缓存机制来存储构建结果,包括编译后的代码、测试结果等。如果检测到某个包或任务自上次构建以来没有变化(基于文件指纹比较),Turborepo 将跳过这些任务的执行,并直接使用缓存中的结果。
云缓存
暂时不过多介绍

Turborepo 核心概念

DependsOn
DependsOn 是一种配置属性,它允许你明确指定任务之间的依赖关系。通过使用 DependsOn,你可以确保在执行某个任务之前,其所有依赖的任务都已经完成。这是构建复杂 Monorepo 项目时确保正确执行顺序的关键机制。
cache
cache 表示是否缓存,通常我们执行 dev 命令的时候会结合 watch 模式,所以我们一般在项目启动模式下不需要开启 turbo 缓存机制
OutputMode
选项
描述
full
显示所有输出(默认)
hash-only
仅显示任务的哈希值
new-only
仅显示缓存未命中的输出
errors-only
仅显示任务失败的输出
none
隐藏所有任务输出
{ "$schema": "https://turborepo.com/schema.json", "ui": "tui", "tasks": { "build": { "dependsOn": ["^build"], <-- DependsOn 依赖关系 "inputs": ["$TURBO_DEFAULT$", ".env*"], "outputs": [".next/**", "!.next/cache/**"], }, "lint": { "dependsOn": ["^lint"] }, "check-types": { "dependsOn": ["^check-types"] }, "dev": { "cache": false, <-- cache 缓存 "persistent": true } } }
 

项目中的落地实践

首先先创建一个事例项目 pnpm dlx create-turbo@latest npx create-next-app@latest pnpm create vue@latest
 
演示build lint 等 任务构建

实施过程中的踩坑与注意事项

问题
解决方式或注意点
输出未配置导致缓存不生效
加上 "outputs": ["dist/**"]
Dev 模式误命中缓存
对 dev 任务配置 "cache": false
多人协作缓存错乱 / CI 无效
使用 turbo remote cache + .turbo 设置
涉及 App / iOS 项目缓存无效
建议 App 项目脱离 turbo 管理,单独部署
输出路径不一致导致命中失败
保证构建产物路径、内容稳定(如路径 hash)

后续优化方向与工程价值