Turborepo logo

倉儲庫的結構

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目錄都將被視為套件。

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

如果您想按目錄群組套件,您可以使用 glob,例如 packages/*packages/group/*,而不要建立 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';

以這種方式使用 exports 提供三大優點

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

imports(選用)

imports 欄位為您提供了一種在套件內建立其他模組子路徑的方式。您可以將這些視為「捷徑」,以寫出更簡單的匯入路徑,這些路徑更能適應移動檔案的重構。若要了解如何操作,請造訪TypeScript 頁面

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

原始碼

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

常見陷阱

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

後續步驟

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