Turborepo

配置任务

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 的基本任务可能如下所示

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

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

按正确的顺序运行任务

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

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

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

但请注意。 此时,您尚未标记构建输出以进行缓存。要执行此操作,请跳转到 指定输出 部分。

依赖具有 ^ 的依赖项中的任务

^ 微语法告诉 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"] 
    }
  }
}

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

没有依赖项

某些任务可能没有任何依赖项。例如,用于在 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 文件将不再被遵循,您需要确保不要使用 glob 捕获这些文件。

要恢复默认行为,请使用 $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 在工作区根目录中的 package.json 中运行脚本。例如,您可能希望除了每个包中的 lint 任务之外,还为工作区根目录中的文件运行 lint:root 任务

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

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

何时使用根任务

  • 工作区根目录的 Linting 和格式化:您可能在工作区根目录中有代码要进行 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 节点 引入到您的任务图中。

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

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

在这个例子中,我们使用了名称 transit - 但您可以将该任务命名为工作区中尚未存在的任何脚本名称。

下一步

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

小时

节省的总计算量
开始使用
远程缓存 →

本页内容