跳到主要內容
/閱讀約 7 分鐘/

Turborepo 入門:獨立開發者也能上手的 Monorepo 架構

Monorepo 不是大公司的專利。搭配 pnpm workspaces 與 Turborepo,一個人也能同時管理多個 app 與共用模組。

目錄+

你手上有一個 Next.js 前端、一個 admin dashboard,還有幾個共用的 utility package。一開始它們分散在不同 repo,每次改一個 shared type,就得跨 repo 發 PR、等 publish、再更新版本號。三個 repo 的 ESLint 與 TypeScript config 也慢慢走樣,最後沒人記得哪個 repo 用的是哪個版本。

Monorepo 解決的正是這個問題。

Monorepo 是什麼

Monorepo 是把多個 project 放進同一個 Git repository,共用一份 dependency tree、一套 config、一條 CI pipeline。它不是把所有 code 塞進同一個資料夾,而是在一個 repo 裡維持清楚的專案邊界。

什麼時候值得用:

  • 你有 2 個以上的 app 共用邏輯(UI 元件、型別、工具函式)
  • 你花太多時間在跨 repo 同步 config 和版本
  • 你希望一個 PR 能同時改到 app 和它依賴的 package

什麼時候不需要:

  • 你只有一個 app,沒有共用模組
  • 團隊成員之間的 codebase 完全獨立
  • 你的 CI/CD 跑不了 monorepo(不過現在幾乎都支援了)

獨立開發者一個人維護 2-3 個 app 加幾個 shared package,monorepo 反而比多 repo 更省事。

Turborepo vs Nx

Monorepo 工具有兩大選擇:Turborepo 和 Nx。

TurborepoNx
定位Task runner + caching完整 monorepo 框架
學習成本低,設定一個 turbo.json中高,有自己的 plugin 系統
設定量極少,幾行 JSON較多,需要理解 project graph
Generator無內建內建 code generator
Plugin 生態無(刻意精簡)豐富(React、Angular、Node 等)
Remote CacheVercel 原生支援Nx Cloud
適合中小型專案、想保持簡單大型專案、需要嚴格的架構約束

Turborepo 的哲學是「把一件事做好」——它只管 task orchestration 和 caching,其餘都交給你自己原本的工具鏈。如果你已經用 pnpm workspaces 跑得好好的,加上 Turborepo 只需要一個設定檔。

Nx 功能更全,但學習曲線也更陡。對獨立開發者或小團隊來說,Turborepo 的簡單本身就是優勢。

從零建立:pnpm workspaces + Turborepo

Step 1:初始化專案

mkdir my-monorepo && cd my-monorepo
pnpm init

建立 pnpm-workspace.yaml:

# pnpm-workspace.yaml
packages:
  - 'apps/*'
  - 'packages/*'

這告訴 pnpm:apps/ 和 packages/ 下的每個資料夾都是一個獨立的 workspace package。

Step 2:安裝 Turborepo

pnpm add -D turbo -w

-w 是安裝到 workspace root。

Step 3:建立目錄結構

my-monorepo/
├── apps/
│   ├── web/              # 主要前端 (Next.js)
│   │   ├── package.json
│   │   └── ...
│   └── admin/            # Admin dashboard (Next.js)
│       ├── package.json
│       └── ...
├── packages/
│   ├── ui/               # 共用 UI 元件
│   │   ├── package.json
│   │   └── src/
│   ├── utils/            # 共用工具函式
│   │   ├── package.json
│   │   └── src/
│   └── config/           # 共用 ESLint、TypeScript config
│       ├── eslint/
│       └── typescript/
├── turbo.json
├── pnpm-workspace.yaml
└── package.json

每個 apps/* 和 packages/* 下的資料夾都有自己的 package.json,定義各自的 name、dependencies 和 scripts。

Step 4:設定 package 之間的依賴

在 apps/web/package.json 裡引用共用 package:

{
  "name": "@apps/web",
  "dependencies": {
    "@packages/ui": "workspace:*",
    "@packages/utils": "workspace:*"
  }
}

workspace:* 是 pnpm 的語法,代表「用 workspace 裡的版本」。不用 publish、不用管版本號,改了立刻生效。

turbo.json:定義 task pipeline

這是 Turborepo 的核心設定:

{
  "$schema": "https://turbo.build/schema.json",
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": [".next/**", "dist/**"]
    },
    "dev": {
      "cache": false,
      "persistent": true
    },
    "lint": {
      "dependsOn": ["^build"]
    },
    "test": {
      "dependsOn": ["build"]
    }
  }
}

幾個關鍵概念:

  • dependsOn: ["^build"] — ^ 代表先跑所有上游依賴的 build。當你執行 turbo run build,Turborepo 會先 build packages/ui、packages/utils,再 build apps/web
  • outputs — 告訴 Turborepo 哪些檔案是 build 產物,用來判斷 cache
  • cache: false — dev 不需要 cache,因為它是 long-running process
  • persistent: true — 標記 dev 是持續執行的 task,不會阻塞其他 task

執行 pnpm turbo run build,Turborepo 會自動解析依賴圖、平行執行,並跳過沒有變更的 package。

常用 packages 拆法

packages/ui

放共用 React 元件。Button、Card、Modal、Input 這些多個 app 都會用到的元件統一放在這裡:

{
  "name": "@packages/ui",
  "exports": {
    "./*": "./src/*.tsx"
  }
}

在 app 裡直接 import:

import { Button } from '@packages/ui/button'

packages/utils

放純函式工具:日期格式化、字串處理、type guards。不要放任何 React 相關的東西,保持 runtime agnostic。

packages/config

放共用設定檔。ESLint config、TypeScript base config、Tailwind preset。每個 app 的 config 都 extend 這裡的 base:

// apps/web/tsconfig.json
{
  "extends": "@packages/config/typescript/nextjs.json",
  "compilerOptions": {
    "outDir": "dist"
  }
}

改一個地方,所有 app 同步更新,不會再出現 A 專案用 strict mode、B 專案沒用的情形。

Remote Caching

Turborepo 最實用的功能之一。本地 cache 只對你自己有效,remote cache 能讓整個 CI 和團隊共享 build 結果。

# 登入 Vercel(Turborepo 的 remote cache 由 Vercel 提供)
pnpm turbo login

# 連結到你的 Vercel team
pnpm turbo link

設定完成後,CI 第一次 build 的結果會上傳到 remote cache。下次任何人(或 CI runner)跑同樣的 build,只要 input 沒變,就直接從 cache 拿結果,build 時間從幾分鐘縮短到幾秒。

對獨立開發者來說,最大的好處是 CI 加速。你的 GitHub Actions 不用每次都重新 build 所有 package。

實際效益

用 Turborepo 管理一個有 3 個 app + 5 個 package 的 monorepo,日常開發的體感差異:

  • 改一個 shared type — 以前:改 package → publish → 更新三個 app 的版本 → 三個 PR。現在:改一次,三個 app 自動拿到
  • 跑 build — 以前:三個 repo 各跑一次。現在:turbo run build,沒改的 package 直接用 cache
  • 加新 app — 以前:從零設定 ESLint、TypeScript、CI。現在:建個資料夾,extend 共用 config,done
  • CI 時間 — 啟用 remote cache 後,只有改到的 package 會重新 build

Monorepo 不是銀彈,但對「一個人維護多個相關專案」的場景,它確實是目前最實用的架構選擇。


想看實際的 monorepo 架構怎麼搭配 Supabase、Polar 和 Next.js 建構完整的學習平台?到 Docs 查看我們的架構設計和實作指南。