儲存庫
文件
開始使用
建立新的單一儲存庫

建立新的單一儲存庫

本指南使用 turbo 的全域安裝。請遵循 安裝指南 來進行設定。或者,您可以在以下命令中使用套件管理員來執行已在本地安裝的 turbo

快速入門

若要建立新的單一儲存庫,請使用我們的 create-turbo(在新分頁中開啟) npm 套件

npx create-turbo@latest

您也可以複製 TurboRepo 初學者儲存庫,讓您的單一儲存庫搶先一步。若要查看 TurboRepo 範例和初學者指南,請參閱 GitHub 上的 TurboRepo 範例目錄(在新分頁中開啟)

完整教學

本教學將引導您設定一個基本範例。完成後,您將有信心使用 turbo,並了解所有基本功能。

在此教學中,一些程式碼行已從程式碼範例中省略。例如,在顯示 package.json 時,我們不會顯示所有的鍵,只會顯示重要的鍵。

1. 執行 create-turbo

首先,執行

npx create-turbo@latest

這會安裝 create-turbo(在新分頁中開啟) CLI,並執行它。系統會詢問你幾個問題

你想要在哪裡建立你的 turborepo?

選擇你喜歡的任何位置。預設為 ./my-turborepo

你想要使用哪個套件管理員?

Turborepo 沒有處理安裝套件,所以你需要選擇下列其中一個

create-turbo 會偵測你的系統上有哪個套件管理員可用。如果你不確定要選擇哪一個,Turborepo 建議 pnpm

安裝

一旦你選擇了套件管理員,create-turbo 會在所選資料夾名稱內建立一堆新檔案。它還會安裝 basic 預設範例附帶的所有相依性。

2. 探索你的新儲存庫

你可能在終端機中注意到某些事項。create-turbo 提供所有新增事項的說明。

>>> Creating a new turborepo with the following:

 - apps/web: Next.js with TypeScript
 - apps/docs: Next.js with TypeScript
 - packages/ui: Shared React component library
 - packages/eslint-config: Shared configuration (ESLint)
 - packages/typescript-config: Shared TypeScript `tsconfig.json`

每個都是一個工作區 - 包含 package.json 的資料夾。每個工作區可以宣告自己的依賴關係、執行自己的指令碼,並匯出其他工作區可使用的程式碼。

在最愛的程式碼編輯器中開啟根資料夾 - ./my-turborepo

了解 packages/ui

首先,開啟 ./packages/ui/package.json。你會注意到套件名稱是 "name": "@repo/ui" - 就在檔案的最上方。

接下來,開啟 ./apps/web/package.json。你會注意到這個套件名稱是 "name": "web"。不過,還要查看它的依賴關係。

你會看到 "web" 依賴一個名為 "@repo/ui" 的套件。

{
  "dependencies": {
    "@repo/ui": "*"
  }
}

這表示我們的網頁應用程式依賴我們的本機 @repo/ui 套件

如果您查看 apps/docs/package.json,您將看到相同內容。webdocs 都依賴於 @repo/ui - 一個共用元件庫。

這種在應用程式間共用程式碼的模式在單一儲存庫中極為常見,這表示多個應用程式可以共用單一設計系統。

了解匯入和匯出

查看 ./apps/docs/app/page.tsx 的內部。docsweb 都是 Next.js(在新分頁中開啟) 應用程式,它們都以類似的方式使用 @repo/ui 函式庫

import { Button } from "@repo/ui/button";
//       ^^^^^^         ^^^^^^^^^^^^^^^
 
export default function Page() {
  return (
    <>
      <Button appName="web" className={styles.button}>
        Click me!
      </Button>
    <>
  );
}

它們直接從名為 @repo/ui/button 的依賴項匯入 Button!它是如何運作的?Button 來自何處?

開啟 packages/ui/package.json。您會注意到 exports 欄位

{
  "exports": {
    "./button": "./src/button.tsx",
    "./card": "./src/card.tsx",
    "./code": "./src/code.tsx"
  },
}

當工作區從 @repo/ui/button 匯入時,exports 會告訴它們在哪裡存取正在匯入的程式碼。

因此,我們來看看 packages/ui/src/button.tsx 中的內容

"use client";
 
import { ReactNode } from "react";
 
interface ButtonProps {
  children: ReactNode;
  className?: string;
  appName: string;
}
 
export const Button = ({ children, className, appName }: ButtonProps) => {
  return (
    <button
      className={className}
      onClick={() => alert(`Hello from your ${appName} app!`)}
    >
      {children}
    </button>
  );
};

我們找到了我們的按鈕!

此檔案中的所有內容都可供依賴 @repo/ui/button 的工作空間使用。

我們在此檔案中所做的任何變更都將在 webdocs 中共用。很酷吧!

嘗試從此檔案中匯出不同的函式。也許 add(a, b) 可用於將兩個數字加總。

然後,這可以由 webdocs 匯入。

了解 tsconfig

我們還有兩個工作空間要查看,typescript-configeslint-config。每個都允許在單一儲存庫中共用設定。我們來看看 typescript-config

{
  "name": "@repo/typescript-config",
}

在這裡,我們看到套件的名稱為 @repo/typescript-config

現在,讓我們來看看位於 web 應用程式的 tsconfig.json 檔案。

{
  "extends": "@repo/typescript-config/nextjs.json",
}

正如您所見,我們正在將 @repo/typescript-config/nextjs.json 直接匯入到我們的 tsconfig.json 檔案中。

此模式允許單一儲存庫在所有工作區中共用單一的 tsconfig.json,減少程式碼重複。

了解 eslint-config

我們的最後一個工作區是 eslint-config

讓我們從查看 packages/eslint-config/package.json 開始

{
  "name": "@repo/eslint-config",
  "files": [
    "library.js",
    "next.js",
    "react-internal.js"
  ],
}

正如您所見,套件的名稱是 @repo/eslint-config,它公開了三個檔案:library.jsnext.jsreact-internal.js

為了了解我們如何使用自訂 ESLint 組態,讓我們查看 apps/docs/.eslintrc.js

module.exports = {
  extends: ["@repo/eslint-config/next.js"],
};

在這裡你可以看到我們直接將 @repo/eslint-config/next.js 匯入到我們的 .eslintrc.js 檔案中。

就像 typescript-config 一樣,eslint-config 讓我們可以在整個 monorepo 中分享 ESLint 的設定檔,不論你正在處理哪個專案,都能保持一致性。

摘要

了解這些工作區之間的相依性非常重要。讓我們來繪製它們的關係圖

  • web - 相依於 uitypescript-configeslint-config
  • docs - 相依於 uitypescript-configeslint-config
  • ui - 相依於 typescript-configeslint-config
  • typescript-config - 沒有相依性
  • eslint-config - 無相依性

請注意,Turborepo CLI 沒有負責管理這些相依性。以上所有事項都由您選擇的套件管理員處理(npmpnpmyarn)。

3. 了解 turbo.json

我們現在了解我們的儲存庫及其相依性。Turborepo 如何提供協助?

Turborepo 透過讓執行任務更簡單且大幅更有效率來提供協助。

讓我們深入了解根目錄中的 turbo.json

{
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": [".next/**", "!.next/cache/**"]
    },
    "lint": {
      "dependsOn": ["^lint"]
    },
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}

我們在這裡看到的是我們已經向 turbo 註冊 三個任務:lintdevbuild。在 turbo.json 內部註冊的每個任務都可以使用 turbo run <task>(或簡寫為 turbo <task>)執行。

在我們繼續之前,讓我們嘗試執行一個名為 hello 的任務,該任務turbo.json 中註冊

turbo hello

您會在終端機看到錯誤訊息。類似

Could not find the following tasks in project: hello

值得注意的是 - 要讓 turbo 執行任務,它必須在 turbo.json 中。

讓我們調查我們已經準備好的腳本。

4. 使用 Turborepo 進行 linting

嘗試執行我們的 lint 腳本

turbo lint

您會注意到在終端機中發生了幾件事。

  1. 會同時執行多個腳本,每個腳本都以 docs:lint@repo/ui:lintweb:lint 為前綴。
  2. 它們都會成功,您會在終端機中看到 3 successful
  3. 您還會看到 0 cached, 3 total。我們稍後會說明這表示什麼意思。

每個執行的腳本都來自每個工作區的 package.json。每個工作區可以選擇指定自己的 lint 腳本

{
  "scripts": {
    "lint": "next lint"
  }
}
{
  "scripts": {
    "lint": "next lint"
  }
}
{
  "scripts": {
    "lint": "eslint \"**/*.ts*\""
  }
}

當我們執行 turbo lint 時,Turborepo 會查看每個工作區中的每個 lint 腳本並執行它。如需更多詳細資訊,請參閱我們的 pipelines 文件。

使用快取

我們再執行一次 lint 這個指令碼。你會注意到終端機中出現一些新的訊息

  1. cache hit, replaying logs 出現在 docs:lintweb:lint@repo/ui:lint
  2. 你會看到 3 cached, 3 total
  3. 總執行時間應低於 100ms,並出現 >>> FULL TURBO

剛才發生了一件有趣的事。Turborepo 發現我們的程式碼自上次執行 lint 指令碼後並未變更

它已儲存前一次執行的記錄,因此只需重新執行它們。

讓我們嘗試變更一些程式碼,看看會發生什麼事。對 apps/docs 中的檔案進行變更

import { Button } from "@repo/ui/button";
//       ^^^^^^         ^^^^^^^^^^^^^^^
 
export default function Page() {
  return (
    <>
      <Button appName="web" className={styles.button}>
-        Click me!
+        Click me now!
      </Button>
    <>
  );
}

現在,再次執行 lint 指令碼。你會注意到

  1. docs:lint 有個註解,寫著 cache miss, executing。這表示 docs 正在執行 linting。
  2. 2 cached, 3 total 出現在底部。

這表示我們先前任務的結果仍被快取。只有 lint 指令碼在 docs 內部實際執行 - 再一次地,加速處理。如需深入了解,請查看我們的 快取文件

5. 使用 Turborepo 建置

我們來試著執行我們的 build 指令碼

turbo build

你會看到與我們執行 lint 指令碼時類似的輸出。只有 apps/docsapps/web 在其 package.json 中指定 build 指令碼,所以只有這些會執行。

turbo.json 中查看 build 的內部。那裡有一些有趣的設定檔。

{
  "pipeline": {
    "build": {
      "outputs": [".next/**", "!.next/cache/**"]
    }
  }
}

您會注意到某些 輸出 已指定。宣告輸出表示當 turbo 完成執行您的工作時,它會將您在快取中指定的輸出儲存起來。

apps/docsapps/web 都是 Next.js 應用程式,它們會將建置輸出至 ./.next 資料夾。

我們來試試看。刪除 apps/docs/.next 建置資料夾。

再次執行 build 指令碼。您會注意到

  1. 我們達到了 FULL TURBO - 建置在不到 100ms 的時間內完成。
  2. .next 資料夾再次出現了!

Turborepo 快取了我們先前建置的結果。當我們再次執行 build 指令時,它從快取中還原了整個 .next/** 資料夾。若要深入了解,請查看我們關於 快取輸出 的文件。

6. 執行開發腳本

現在讓我們嘗試執行 dev

turbo dev

您會在終端機中看到一些資訊

  1. 只有兩個腳本會執行 - docs:devweb:dev。這兩個工作區是唯一指定 dev 的。
  2. 兩個 dev 腳本會同時執行,在埠 30003001 上啟動您的 Next.js 應用程式。
  3. 在終端機中,您會看到 快取略過,強制執行

嘗試退出腳本,然後重新執行。您會注意到我們不會進入 FULL TURBO。這是為什麼?

看看 turbo.json

{
  "pipeline": {
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}

dev 中,我們指定了 "cache": false。這表示我們告訴 Turborepo 不要快取 dev 腳本的結果。 dev 執行持續的開發伺服器,且不產生任何輸出,因此沒有任何東西可以快取。在我們的文件 關閉快取 中深入了解它。

此外,我們設定 "persistent": true,讓 turbo 知道這是一個長時間執行的開發伺服器,以便 turbo 能確保沒有其他任務依賴於它。您可以在 persistent 選項的文件 中閱讀更多內容。

一次只在一個工作區上執行 dev

預設情況下,turbo dev 會同時在所有工作區上執行 dev。但有時,我們可能只想選擇一個工作區。

為了解決這個問題,我們可以在命令中新增 --filter 旗標。

turbo dev --filter docs

您會注意到,現在它只執行 docs:dev。從我們的文件中深入了解 過濾工作區

摘要

做得好!您已了解有關新單一儲存庫的所有資訊,以及 Turborepo 如何讓您更容易處理任務。

後續步驟