配置任务

Turborepo 将始终按照你的 turbo.json 配置文件包图 中描述的顺序运行任务,并在可能的情况下并行处理工作,以确保一切尽可能快地运行。这比一次运行一个任务更快,也是 Turborepo 如此快速的部分原因。

例如,yarn workspaces run lint && yarn workspaces run test && yarn workspaces run build 看起来像这样

A graphical representation of `turbo run lint test build`. It shows all tasks running in parallel, with much less empty space where scripts are not being ran.

但是,为了使用 Turborepo 更快地完成相同的工作,你可以使用 turbo run lint test build

A graphical representation of `turbo run lint test build`. It shows all tasks running in parallel, with much less empty space where scripts are not being ran.

开始使用

根目录的 turbo.json 文件是你注册 Turborepo 将要运行的任务的地方。一旦你定义了你的任务,你就可以使用 turbo run 运行一个或多个任务。

  • 如果你是全新开始,我们建议 使用 create-turbo 创建一个新的仓库 并编辑 turbo.json 文件来试用本指南中的代码片段。
  • 如果你要在现有仓库中采用 Turborepo,请在你的仓库根目录中创建一个 turbo.json 文件。你将使用它来了解本指南中其余的配置选项。
turbo.json
package.json

定义任务

tasks 对象中的每个键都是可以通过 turbo run 执行的任务。Turborepo 将在你的包中搜索 package.json 中脚本名称与任务名称相同的脚本

要定义一个任务,请使用 turbo.json 中的 tasks 对象。例如,一个名为 build 的基本任务,没有依赖项和输出,可能看起来像这样

Turborepo logo
./turbo.json
{
  "tasks": {
    "build": {} // Incorrect!
  }
}

如果你此时运行 turbo run build,Turborepo 将并行运行你的包中的所有 build 脚本,并且不会缓存任何文件输出。这会很快导致错误。 你还缺少一些重要的部分,才能使其按你期望的方式工作。

以正确的顺序运行任务

dependsOn 用于指定在另一个任务开始运行之前必须完成的任务。例如,在大多数情况下,你希望你的库的 build 脚本在你的应用程序的 build 脚本运行之前完成。为此,你可以使用以下 turbo.json

Turborepo logo
./turbo.json
{
  "tasks": {
    "build": {
      "dependsOn": ["^build"] 
    }
  }
}

你现在拥有了你期望的构建顺序,在 依赖项 之前构建 依赖者

但要小心。 此时,你还没有标记用于缓存的构建输出。要做到这一点,请跳转到 指定输出 部分。

依赖于依赖项中的任务,使用 ^

^ 微语法告诉 Turborepo 从依赖关系图的底部开始运行任务。如果你的应用程序依赖于一个名为 ui 的库,并且该库有一个 build 任务,那么 ui 中的 build 脚本将首先运行。一旦它成功完成,你的应用程序中的 build 任务将运行。

这是一个重要的模式,因为它确保你的应用程序的 build 任务将拥有编译所需的所有必要依赖项。当你的依赖关系图增长到具有更多级别的任务依赖关系的更复杂结构时,此概念也适用。

依赖于同一包中的任务

有时,你可能需要确保同一包中的两个任务以特定顺序运行。例如,你可能需要在你的库中运行 build 任务,然后再在同一库中运行 test 任务。为此,请在 dependsOn 键中将脚本指定为纯字符串(不带 ^)。

Turborepo logo
./turbo.json
{
  "tasks": {
    "test": {
      "dependsOn": ["build"] 
    }
  }
}

依赖于特定包中的特定任务

你还可以指定要依赖的特定包中的单个任务。在下面的示例中,utils 中的 build 任务必须在任何 lint 任务之前运行。

Turborepo logo
./turbo.json
{
  "tasks": {
    "lint": {
      "dependsOn": ["utils#build"] 
    }
  }
}

你也可以更具体地说明依赖任务,将其限制为某个特定的包

Turborepo logo
./turbo.json
{
  "tasks": {
    "web#lint": {
      "dependsOn": ["utils#build"] 
    }
  }
}

使用此配置,只有在 utils 包中的 build 任务完成后,才能运行你的 web 包中的 lint 任务。

没有依赖项

某些任务可能没有任何依赖项。例如,用于查找 Markdown 文件中拼写错误的任务可能不需要关心其他任务的状态。在这种情况下,你可以省略 dependsOn 键或提供一个空数组。

Turborepo logo
./turbo.json
{
  "tasks": {
    "spell-check": {
      "dependsOn": [] 
    }
  }
}

指定 outputs

Turborepo 缓存你的任务的输出,这样你就永远不会做两次相同的工作。我们将在 缓存指南 中深入讨论这一点,但让我们首先确保你的任务已正确配置。

outputs 键告诉 Turborepo 在任务成功完成后应该缓存的 文件和目录如果没有定义此键,Turborepo 将不会缓存任何文件。在后续运行中命中缓存将不会恢复任何文件输出。

以下是一些常见工具的输出示例

Turborepo logo
./turbo.json
{
  "tasks": {
    "build": {
      "outputs": [".next/**", "!.next/cache/**"] 
    }
  }
}

Glob 相对于包,因此 dist/** 将分别处理每个包输出的 dist。有关为 outputs 键构建 glob 模式的更多信息,请参阅 glob 规范

指定 inputs

inputs 键用于指定你想要包含在任务哈希中的文件,以便进行 缓存。默认情况下,Turborepo 将包括包中 Git 跟踪的所有文件。但是,你可以使用 inputs 键更具体地说明哪些文件包含在哈希中。

例如,用于查找 Markdown 文件中拼写错误的任务可以像这样定义

Turborepo logo
./turbo.json
{
  "tasks": {
    "spell-check": {
      "inputs": ["**/*.md", "**/*.mdx"] 
    }
  }
}

现在,只有 Markdown 文件中的更改才会导致 spell-check 任务错过缓存。

此功能选择退出 Turborepo 的所有默认 inputs 行为,包括跟踪源代码控制所跟踪的更改。这意味着你的 .gitignore 文件将不再被尊重,你需要确保你不会使用 glob 捕获这些文件。

要恢复默认行为,请使用 $TURBO_DEFAULT$ 微语法

使用 $TURBO_DEFAULT$ 恢复默认值

默认的 inputs 行为 通常是你的任务所需要的。但是,你可以通过微调你的 inputs 以忽略已知不会影响任务输出的文件更改,从而提高你的缓存命中率。

因此,你可以使用 $TURBO_DEFAULT$ 微语法来微调默认的 inputs 行为

Turborepo logo
./turbo.json
{
  "tasks": {
    "build": {
      "inputs": ["$TURBO_DEFAULT$", "!README.md"] 
    }
  }
}

在此任务定义中,Turborepo 将对 build 任务使用默认的 inputs 行为,但将忽略对 README.md 文件的更改。如果 README.md 文件被更改,该任务仍然会命中缓存。

注册根任务

你还可以使用 turbo 在工作区根目录的 package.json 中运行脚本。例如,你可能想要为工作区根目录中的文件运行 lint:root 任务,以及每个包中的 lint 任务

Turborepo logo
./turbo.json
{
  "tasks": {
    "lint": {
      "dependsOn": ["^lint"]
    },
    "//#lint:root": {} 
  }
}

现在已注册根任务,turbo run lint:root 现在将运行该任务。你还可以运行 turbo run lint lint:root 来运行你所有的 lint 任务。

何时使用根任务

  • 工作区根目录的 Lint 和格式化:你可能在工作区根目录中有一些代码想要进行 lint 和格式化。例如,你可能想在你的根目录中运行 ESLint 或 Prettier。
  • 增量迁移:当你迁移到 Turborepo 时,你可能有一个中间步骤,其中你有一些尚未移动到包的脚本。在这种情况下,你可以创建一个根任务来开始迁移,并在稍后将任务分散到包中。
  • 没有包范围的脚本:你可能有一些在特定包的上下文中没有意义的脚本。这些脚本可以注册为根任务,这样你仍然可以使用 turbo 运行它们,以实现缓存、并行化和工作流目的。

高级用例

使用包配置

包配置 是直接放置在包中的 turbo.json 文件。这允许包为其自己的任务定义特定行为,而不会影响仓库的其余部分。

在拥有许多团队的大型 monorepo 中,这允许团队对其自己的任务进行更大的控制。要了解更多信息,请访问 包配置文档

具有运行时依赖的长时任务

你可能有一个长时间运行的任务,需要另一个任务始终同时运行。为此,请使用 with

Turborepo logo
./apps/web/turbo.json
{
  "tasks": {
    "dev": {
      "with": ["api#dev"],
      "persistent": true,
      "cache": false
    }
  }
}

长时间运行的任务永远不会退出,这意味着你不能依赖它。相反,with 关键字将在 web#dev 任务运行时运行 api#dev 任务。

执行副作用

某些任务应该始终运行,无论如何,例如缓存构建后的部署脚本。对于这些任务,请在你的任务定义中添加 "cache": false

Turborepo logo
./turbo.json
{
  "tasks": {
    "deploy": {
      "dependsOn": ["^build"],
      "cache": false
    },
    "build": {
      "outputs": ["dist/**"]
    }
  }
}

可以并行运行的依赖任务

某些任务即使依赖于其他包也可以并行运行。符合此描述的任务示例是 linters,因为 linter 不需要等待依赖项中的输出成功运行。

因此,你可能会倾向于像这样定义你的 check-types 任务

Turborepo logo
./turbo.json
{
  "tasks": {
    "check-types": {} // Incorrect!
  }
}

这会并行运行你的任务 - 但不会考虑依赖项中的源代码更改。这意味着你可以

  1. 对你的 ui 包的接口进行破坏性更改。
  2. 运行 turbo check-types,在一个依赖于 ui 的应用程序包中命中缓存。

这是不正确的,因为应用程序包将显示成功的缓存命中,尽管它没有更新以使用新的接口。在你的编辑器中手动检查应用程序包中的 TypeScript 错误可能会显示错误。

因此,你对你的 check-types 任务定义进行了一个小的更改

Turborepo logo
./turbo.json
{
  "tasks": {
    "check-types": {
      "dependsOn": ["^check-types"] // This works...but could be faster!
    }
  }
}

如果你再次测试在你的 ui 包中进行破坏性更改,你将注意到缓存行为现在是正确的。但是,任务不再并行运行。

为了满足这两个要求(正确性和并行性),你可以将 Transit Nodes 引入到你的任务图中

Turborepo logo
./turbo.json
{
  "tasks": {
    "transit": {
      "dependsOn": ["^transit"]
    },
    "check-types": {
      "dependsOn": ["transit"]
    }
  }
}

这些 Transit Nodes 使用一个不执行任何操作的任务在你的包依赖项之间创建关系,因为它与任何 package.json 中的脚本都不匹配。因此,你的任务可以并行运行 并且 意识到对其内部依赖项的更改。

在本例中,我们使用了名称 transit - 但你可以将任务命名为任何尚未成为你的工作区中脚本的名称。

下一步

配置 turbo.json 文档 中提供了更多选项,你将在接下来的指南中进行探索。现在,你可以开始运行一些任务,看看基本原理是如何工作的。