包和任务图(Package and Task Graph)

包图(Package Graph)

包图是由你的包管理器创建的单体仓库结构。当你将内部包相互安装时,Turborepo 会自动识别这些依赖关系,以建立对你的工作空间的基本理解。

这为任务图奠定了基础,在任务图中你将定义任务之间如何相互关联。

任务图(Task Graph)

turbo.json 中,你表达了任务之间如何相互关联。你可以将这些关系视为任务之间的依赖关系,但我们有一个更正式的名称:任务图。

Good to know: 

你可以使用 --graph 标志 为你的任务生成任务图的可视化。

Turborepo 使用一种称为有向无环图 (DAG)的数据结构来理解你的仓库及其任务。图由"节点"和"边"组成。在任务图中,节点是任务,边是任务之间的依赖关系。有向图表示连接每个节点的边都有一个方向,所以如果任务 A 指向任务 B,我们可以说任务 A 依赖于任务 B。边的方向取决于哪个任务依赖于哪个。

例如,假设你有一个单体仓库,其中 ./apps/web 中的应用程序依赖于两个包:@repo/ui@repo/utils

你还有一个依赖于 ^buildbuild 任务:

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

Turborepo 将构建如下的任务图:

任务图可视化。图表顶部有一个名为 "apps/web" 的节点,有两条线分别连接到其他节点 "packages/ui" 和 "packages/utils"。

传递节点

构建任务图时的一个挑战是处理嵌套依赖。例如,假设你的单体仓库有一个 docs 应用程序,它依赖于 ui 包,而 ui 包又依赖于 core 包:

让我们假设 docs 应用程序和 core 包各有一个 build 任务,但 ui 包没有。你还有一个 turbo.json,它像上面一样配置了 build 任务,使用 "dependsOn": ["^build"]。当你运行 turbo run build 时,你期望会发生什么?

Turborepo 将构建这个任务图:

带有传递节点的任务图可视化。图表顶部有一个名为 "apps/doc" 的节点,有一条线连接到 "packages/ui" 节点。这个节点没有 "build" 任务。"packages/ui" 节点有另一条线连接到有 "build" 任务的 "packages/core" 节点。

你可以将这个图想象成一系列步骤:

  • docs 应用程序只依赖于 ui
  • ui没有 build 脚本。
  • ui 包的依赖项有 build 脚本,所以任务图知道要包含这些。

在这种情况下,Turborepo 将 ui 包称为传递节点,因为它没有自己的 build 脚本。由于它没有 build 脚本,Turborepo 不会为它执行任何操作,但它仍然是图的一部分,目的是包含它自己的依赖项。

作为入口点的传递节点

如果 docs/ 包没有实现 build 任务会怎样?在这种情况下你期望会发生什么?uicore 包是否应该仍然执行它们的构建任务?是否应该发生任何事情

Turborepo 的心智模型是任务图中的所有节点都是相同的。换句话说,无论传递节点在图中出现在哪里,它们都会被包含在图中。这个模型可能会产生意想不到的后果。例如,假设你已将 build 任务配置为依赖于 ^test

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

假设你的单体仓库有许多应用程序和许多包。所有包都有 test 任务,但只有一个应用程序有 build 任务。Turborepo 的心智模型表明,当你运行 turbo run build 时,即使应用程序没有实现 build,所有作为依赖项的包的 test 任务都会出现在图中。