Turborepo

組織儲存庫

turbo 建構於 工作區 之上,這是 JavaScript 生態系中套件管理工具的一項功能,可讓您將多個套件分組在一個儲存庫中。

遵循這些慣例非常重要,因為這可讓您:

  • 在您儲存庫的所有工具中利用這些慣例
  • 快速地將 Turborepo 逐步導入現有的儲存庫

在本指南中,我們將逐步說明如何設定多套件工作區(monorepo),以便為 turbo 奠定基礎。

開始使用

手動設定工作區的結構可能很繁瑣。如果您是 monorepo 的新手,我們建議使用 create-turbo 立即開始,並建立有效的工作區結構。

終端機
npx create-turbo@latest

然後,您可以檢查儲存庫是否具有本指南中描述的特性。

工作區的結構

在 JavaScript 中,工作區可以是單一套件,也可以是多個套件的集合。在這些指南中,我們將重點放在多套件工作區,通常稱為「monorepo」。

下方將重點說明 create-turbo 的結構元素,使其成為有效的工作區。

package.json
package-lock.json
turbo.json
package.json

最低需求

在 monorepo 中指定套件

宣告套件的目錄

首先,您的套件管理工具需要描述套件的位置。我們建議從將套件分割成 apps/(用於應用程式和服務)和 packages/(用於其他所有內容,例如函式庫和工具)開始。

./package.json
{
  "workspaces": [
    "apps/*",
    "packages/*"
  ]
}
npm 工作區文件

使用此組態,在 appspackages 目錄中,每個具有 package.json目錄都會被視為一個套件。

由於 JavaScript 生態系中套件管理工具之間存在不明確的行為,因此 Turborepo 不支援巢狀套件,例如 apps/**packages/**。使用將套件放在 apps/a 和另一個放在 apps/a/b 的結構將會導致錯誤。

如果您想要依目錄分組套件,可以使用類似 packages/*packages/group/* 的 glob,而不是建立 packages/group/package.json 檔案。

每個套件中的 package.json

在套件的目錄中,必須有 package.json 才能讓套件對套件管理工具和 turbo 而言是可探索的。下方列出套件的 package.json 的需求

package.json

package.json 是您工作區的基礎。下方是您會在根 package.json 中找到的常見範例

./package.json
{
  "private": true,
  "scripts": {
    "build": "turbo run build",
    "dev": "turbo run dev",
    "lint": "turbo run lint"
  },
  "devDependencies": {
    "turbo": "latest"
  },
  "packageManager": "npm@10.0.0"
}

turbo.json

turbo.json 用於設定 turbo 的行為。若要深入瞭解如何設定任務,請造訪設定任務頁面。

套件管理工具鎖定檔

鎖定檔是您的套件管理工具和 turbo 可重複行為的關鍵。此外,Turborepo 使用鎖定檔來了解您工作區中內部套件之間的相依性。

如果您在執行 turbo 時沒有鎖定檔,可能會看到無法預期的行為。

套件的結構

最好開始將設計套件視為工作區內自己的單元。在高階層次上,每個套件幾乎都像是自己的小型「專案」,具有自己的 package.json、工具組態和原始碼。這個想法有一些限制,但它是個很好的起點

此外,套件具有特定的進入點,工作區中的其他套件可以使用這些進入點來存取套件,並由 exports 指定。

套件的 package.json

name

name 欄位用於識別套件。它在您的工作區中應是唯一的。

最佳做法是為您的內部套件使用命名空間字首,以避免與 npm 登錄檔上的其他套件發生衝突。例如,如果您的組織名稱是 acme,您可能會將您的套件命名為 @acme/package-name

我們在我們的文件和範例中使用 @repo,因為它在 npm 登錄檔上是未使用且無法宣告的命名空間。您可以選擇保留它或使用您自己的字首。

scripts

scripts 欄位用於定義可以在套件的內容中執行的指令碼。Turborepo 將使用這些指令碼的名稱來識別要在套件中執行哪些指令碼(如果有的話)。我們在執行任務頁面上會進一步討論這些指令碼。

exports

exports 欄位用於指定想要使用套件的其他套件的進入點。當您想要使用一個套件中的程式碼到另一個套件時,您會從該進入點匯入。

例如,如果您有一個 @repo/math 套件,您可能會具有下列 exports 欄位

./packages/math/package.json
{
  "exports": {
    ".": "./src/constants.ts",
    "./add": "./src/add.ts",
    "./subtract": "./src/subtract.ts"
  }
}

請注意,此範例為了簡化起見使用了即時套件模式。它直接匯出 TypeScript,但您可以選擇改用編譯套件模式。

此範例中的 exports 欄位需要 Node.js 和 TypeScript 的現代版本。

這可讓您從 @repo/math 套件匯入 addsubtract 函式,如下所示

./apps/my-app/src/index.ts
import { GRAVITATIONAL_CONSTANT, SPEED_OF_LIGHT } from '@repo/math';
import { add } from '@repo/math/add';
import { subtract } from '@repo/math/subtract';

以這種方式使用匯出可帶來三個主要優點

  • 避免使用 barrel 檔案:Barrel 檔案是重新匯出同一個套件中其他檔案的檔案,為整個套件建立一個進入點。雖然它們看起來很方便,但編譯器和 bundler 很難處理,而且可能會快速導致效能問題。
  • 更強大的功能:與 main 欄位相比,exports 也具有其他強大功能,例如條件匯出。一般而言,我們建議盡可能使用 exports 而不是 main,因為它是比較現代的選項。
  • IDE 自動完成:透過使用 exports 指定套件的進入點,您可以確保您的程式碼編輯器可以為套件的匯出提供自動完成功能。

好消息

您也可以使用萬用字元指定 exports。不過,您會因為 TypeScript 編譯器的效能取捨而失去一些 IDE 自動完成功能。如需詳細資訊,請造訪TypeScript 指南

imports(選用)

imports 欄位可讓您建立套件中其他模組的子路徑。您可以將這些子路徑視為「捷徑」,以撰寫更簡單的匯入路徑,更能夠復原移動檔案的重構。若要瞭解如何操作,請造訪TypeScript 頁面

您可能更熟悉 TypeScript 的 compilerOptions#paths 選項,該選項可達成類似的目標。從 TypeScript 5.4 開始,TypeScript 可以從 imports 推斷子路徑,使其成為更好的選項,因為您將使用 Node.js 慣例。如需詳細資訊,請造訪我們的 TypeScript 指南

原始碼

當然,您會在套件中需要一些原始碼。套件通常會使用 src 目錄來儲存其原始碼,並編譯到 dist 目錄(也應該位於套件中),儘管這不是必要條件。

常見的陷阱

  • 如果您使用 TypeScript,您可能不需要在工作區的根目錄中具有 tsconfig.json。套件應該獨立指定自己的組態,通常是從工作區中個別套件的共用 tsconfig.json 進行建置。如需詳細資訊,請造訪TypeScript 指南
  • 您希望盡可能避免跨套件界限存取檔案。如果您發現自己正在寫入 ../ 以從一個套件移到另一個套件,您可能會找到機會重新思考您的方法,方法是在需要的地方安裝套件,並將其匯入您的程式碼。

後續步驟

設定好您的工作區後,您現在可以使用您的套件管理器將相依性安裝到您的套件中

小時

總共節省的運算量
開始使用
遠端快取 →

本頁內容