Turborepo logo

配置任务

Turborepo 将始终按照你的 turbo.json 配置文件Package Graph 中描述的顺序运行任务,并在可能的情况下并行处理工作,以确保一切尽可能快地运行。这比一次运行一个任务更快,也是 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 的基本任务可能看起来像这样

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

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

以正确的顺序运行任务

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

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

你现在有了你期望的构建顺序,在 dependents 之前构建 dependencies

但是要小心。 此时,你尚未标记构建输出以进行缓存。为此,请跳转到 指定输出 部分。

依赖于带有 ^ 符号的依赖项中的任务

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

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

依赖于同一包中的任务

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

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

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

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

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

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

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

通过此配置,你的 web 包中的 lint 任务只能在 utils 包中的 build 任务完成后运行。

无依赖项

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

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

指定 outputs

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

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

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

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

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

指定 inputs

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

例如,用于查找 Markdown 文件中错别字的任务可以这样定义

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

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

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

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

使用 $TURBO_DEFAULT$ 恢复默认值

默认的 inputs 行为 通常是你希望用于任务的行为。但是,你可以通过微调你的 inputs 以忽略已知不会影响任务输出的文件的更改,来提高某些任务的缓存命中率。

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

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

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

注册根任务

你还可以使用 turbo 在 Workspace 根目录的 package.json 中运行脚本。例如,除了每个包中的 lint 任务之外,你可能还希望为 Workspace 根目录中的文件运行 lint:root 任务

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

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

何时使用根任务

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

高级用例

使用包配置

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

在拥有许多团队的大型 monorepo 中,这使团队能够更好地控制自己的任务。要了解更多信息,请访问 包配置文档

执行副作用

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

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

可以并行运行的依赖任务

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

因此,你可能会想这样定义你的 check-types 任务

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

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

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

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

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

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

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

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

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

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

在此示例中,我们使用了名称 transit - 但你可以将任务命名为 Workspace 中尚未存在的任何脚本。

下一步

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