Turborepo 非 Monorepo 实战:用 turbo 任务编排与缓存管理单个 Next.js 应用
2026/9/20 2:56:21 网站建设 项目流程

Turborepo 非 Monorepo 实战:用 turbo 任务编排与缓存管理单个 Next.js 应用

【免费下载链接】turboBuild system optimized for JavaScript and TypeScript, written in Rust项目地址: https://gitcode.com/gh_mirrors/tu/turbo

导读

Turborepo 通常与「monorepo(多包仓库)」绑定出现,但它的任务编排与增量缓存能力同样适用于单个项目的代码仓库。本文以官方仓库中由核心团队维护的 non-monorepo 示例 为骨架,完整讲解如何用turbo管理一个独立的 Next.js 应用:包括create-turbo的脚手架方式、turbo.json中四个核心任务的配置语义、以及$TURBO_DEFAULT$persistent等关键机制在源码层面的实现原理。读完本文,你将掌握在非 monorepo 场景下落地 Turborepo 任务缓存与长期运行任务管理的完整方案。

什么是 Turborepo non-monorepo starter

这个示例(目录为 examples/non-monorepo)演示了一个核心事实:Turborepo 并不强制要求多包结构。它使用 Turborepo 管理一个单一、非 monorepo 的项目——在这里是一个单独的 Next.js 应用程序。

在该目录的 meta.json 中,官方将它的定位描述为:

{ "name": "Non-monorepo", "description": "A standalone application using Turborepo", "maintainedByCoreTeam": true }

maintainedByCoreTeam: true表明这是由 Turborepo 核心团队直接维护的示例,可作为生产实践的权威参照。

快速开始

与仓库中其他示例(basicwith-nestjswith-svelte等)一致,non-monorepo 同样通过create-turbo脚手架模板创建,命令为:

npx create-turbo@latest -e non-monorepo

-e(即--example)参数指定从该模板生成项目。创建完成后,目录中只包含一个单包应用,没有任何packages/工作区目录——这与 basic 示例 的 monorepo 结构形成了鲜明对照。

项目结构剖析

整个模板的文件布局非常精简:

non-monorepo/ ├── app/ │ ├── favicon.ico │ ├── globals.css │ ├── layout.tsx │ └── page.tsx ├── public/ # Next.js 静态资源 ├── eslint.config.mjs ├── next.config.ts ├── package.json ├── package-lock.json ├── postcss.config.mjs ├── tsconfig.json └── turbo.json

根目录的 package.json 定义了应用自身的脚本:

{ "scripts": { "dev": "next dev", "build": "next build", "start": "next start", "lint": "eslint", "check-types": "next typegen && tsc --noEmit" } }

这里有个值得注意的细节:check-typesnext typegentsc --noEmit两步组合而成,而不是简单的tsc --noEmit。同时 next.config.ts 中配置了typescript.ignoreBuildErrors: trueexperimental.useTypeScriptCli: false,其注释解释了原因:该项目并行运行 TypeScript 7(tsc)与 TypeScript 6 API(typescript),为了让 typescript-eslint 等工具正常工作,Next.js 需要加载 TypeScript 6 API 而非 TypeScript 7 CLI 来执行自身检查。这提醒我们:当 turbo 任务对接的底层工具链发生变化时,任务脚本的拆分与配置需要联动调整

Turborepo 在这里扮演的角色,就是将这些分散的 npm scripts 统一为turbo buildturbo lintturbo check-typesturbo dev四个可缓存的、可编排的任务。

turbo.json 任务配置详解

non-monorepo 示例的核心配置位于 turbo.json:

{ "$schema": "https://turborepo.dev/schema.json", "ui": "tui", "tasks": { "build": { "inputs": ["$TURBO_DEFAULT$", ".env*"], "outputs": [".next/**", "!.next/cache/**", "!.next/dev/**"] }, "lint": {}, "check-types": {}, "dev": { "cache": false, "persistent": true } } }

下面逐项拆解。

build:定义缓存输入与输出

build任务通过inputsoutputs两个字段声明了 Turborepo 增量缓存的核心边界:

  • inputs:声明参与任务哈希计算的输入文件集合。["$TURBO_DEFAULT$", ".env*"]的含义是——对包目录下的所有文件($TURBO_DEFAULT$的展开语义见下文源码解析)进行哈希,并额外把根目录的.env*环境文件纳入哈希。这意味着修改.env.env.local等文件同样会使build任务哈希失效、触发重新构建。
  • outputs:声明任务产物,用于命中缓存后的恢复。这里输出是.next/**,同时用!前缀排除.next/cache/**.next/dev/**——这两类文件是 Next.js 自身的缓存与开发产物,不应被 Turbo 缓存或恢复。

由于单包项目不存在包间依赖,build任务没有声明dependsOn: ["^build"]。对比 basic 示例的 turbo.json(monorepo 版)中buildlintcheck-types都带有dependsOn: ["^build"]等拓扑依赖声明,可以清晰看出:非 monorepo 场景下任务依赖被大幅简化,turbo 的任务图退化为单节点执行,这正是「单项目也能用 turbo」的关键原因。

lint 与 check-types:零配置即可获得缓存

lintcheck-types两个任务都是空对象{}。这并非占位符,而是 Turborepo 的零配置缓存约定:未声明outputs时,Turborepo 默认将dist/**build/**(以及其他按约定推导的常见产物目录)作为输出进行缓存。因此即便不写任何配置,turbo lintturbo check-types也能获得「输入未变化则直接回放结果」的缓存能力,只是由于这两个任务通常不产生持久化产物,缓存的收益更多体现在「跳过重复执行」上。

dev:长期运行任务的正确姿势

dev任务的两个字段在非 monorepo 场景中最容易被忽视,却恰恰是最重要的:

  • cache: false:开发服务器是交互式、有副作用的进程,产物不可复用,因此明确关闭缓存。
  • persistent: true:声明这是一个**长期运行(persistent)**的任务——它不会自行退出,而是持续监听文件变化提供热更新服务。这个标记会触发 Turborepo 引擎的特殊处理(详见下文源码解析)。

四个核心命令的实战操作

按照官方 README 的说明,模板已预置好四个可直接使用的任务。

构建应用

npx turbo build

触发 Next.js 生产构建(等价于next build),产物写入.next/。首次运行会构建并写入缓存,之后只要inputs声明范围内的文件(含.env*)没有变化,再次执行将从缓存直接恢复结果。

Lint 源码

npx turbo lint

执行 ESLint 检查(等价于eslint)。本模板的 eslint.config.mjs 采用了 flat config 写法,组合了eslint-config-next/core-web-vitalseslint-config-next/typescript,并通过withoutReactPlugin适配 ESLint 10(eslint-plugin-react 尚不支持时剔除其插件与react/前缀规则),同时用globalIgnores覆盖.next/**out/**build/**next-env.d.ts等默认忽略项。

类型检查

npx turbo check-types

执行next typegen && tsc --noEmit两步类型检查。next typegen生成路由等类型信息,随后tsc --noEmit做纯类型校验。

启动开发服务器

npx turbo dev

启动 Next.js 开发服务器(等价于next dev)。由于配置了cache: falsepersistent: true,该任务不会被打断、不会被缓存,并且会得到 Turborepo 针对长期运行任务的专门调度处理。

源码级的机制解析:从配置到引擎

以上配置项并非黑盒魔法,其语义在 Rust 实现的 Turborepo 引擎中有明确对应。

$TURBO_DEFAULT$:包目录的哈希边界

在 crates/turborepo-lib/src/run/builder.rs 的untracked_scan_prefixes注释中,Turborepo 明确了哈希扫描的范围规则:

对于每个参与任务,其哈希基于包目录进行文件哈希;任务未声明inputs或声明了$TURBO_DEFAULT$时,会对包目录下的所有内容进行哈希

也就是说,inputs: ["$TURBO_DEFAULT$", ".env*"]实际上是在「默认全量哈希」的基础上叠加了.env*的额外覆盖。同一段注释还指出:当哈希输入可能越过包目录边界(如根任务相对于仓库根目录哈希、globalDependencies覆盖全仓库、或$TURBO_ROOT$/..形式的 glob)时,Turbo 会拒绝做「仅包内」的范围优化。这解释了为何 non-monorepo 示例中的任务全部保持「包内哈希」的简洁形态——单项目仓库中,包目录即仓库根目录,哈希边界天然完整。

persistent 任务的引擎约束

persistent: true并不是一句装饰性声明。在 crates/turborepo-lib/src/engine/mod.rs 中,引擎在构建任务图时会校验:

  • persistent 任务不能作为其他任务的依赖(报错"{persistent_task}" is a persistent task, "{dependant}" cannot depend on it);
  • 当存在 persistent 任务时,会对并发任务数量提出约束(You have {persistent_count} persistent tasks but turbo is configured for concurrency ...)。

从工程语义上理解:dev服务器需要与buildlint等一次性任务共存于任务图中,但前者永不退出,后者必须等待其完成——两者天然冲突。Turbo 通过persistent标记将长期运行任务隔离调度,避免任务图死锁。在 cli/args.rs 中也有对应提示,将persistentwith参数(等待指定任务完成的运行模式)关联说明。这也是官方 README 中强调npx turbo dev可放心使用的原因。

与 monorepo 示例的差异速览

维度non-monorepo(本文)basic(monorepo 示例)
包结构单个 Next.js 应用多个 apps + packages 工作区
dependsOn: ["^build"]无(无包间依赖)有(需要拓扑排序)
哈希范围单包目录(即仓库根目录)各包目录 + 根依赖折叠
使用场景单项目也想获得任务缓存与统一命令多包依赖编排与增量构建

两者最大的共同点是:turbo的缓存与任务编排能力与仓库形态解耦。即便未来项目规模增长、需要拆分为 monorepo,现有turbo.json中的build/lint/check-types/dev任务定义也能平滑迁移。

小结

通过 examples/non-monorepo 这份官方示例可以看到:Turborepo 的价值不限于大型 monorepo,它同样为单项目仓库提供了「统一任务入口 + 增量缓存 + 长期任务管理」的轻量方案。实践要点可归纳为:

  1. npx create-turbo@latest -e non-monorepo一键生成模板;
  2. build声明inputsoutputs,让缓存边界精确可复用;
  3. lint/check-types留空即可获得默认缓存行为;
  4. 开发类任务务必声明cache: false+persistent: true,以适配引擎对长期运行任务的特殊调度;
  5. 理解$TURBO_DEFAULT$与 persistent 校验(对应源码 builder.rs 与 engine/mod.rs),才能在配置变更时做出正确判断。

【免费下载链接】turboBuild system optimized for JavaScript and TypeScript, written in Rust项目地址: https://gitcode.com/gh_mirrors/tu/turbo

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询