npm workspaces 已經夠強了,為何還需要 MonoRepo 方案?

Sean發表於2024-12-05

隨著專案規模的增長和模組化需求的增強,MonoRepo(單一程式碼庫)的管理方式正在成為熱門選擇。本文將帶領你瞭解 MonoRepo 的形成背景,探討主流解決方案(如 pnpm workspaces、TurboRepo、Nx 和 Rush),並透過對比分析這些工具的優劣與適用場景,最終幫助你選擇最適合的工具。

MonoRepo 的形成背景

隨著專案複雜度和團隊規模的擴大,傳統的單體程式碼庫或多程式碼庫(PolyRepo)模式逐漸暴露出以下問題:

  • 依賴管理複雜: 各專案間共享依賴需要人工管理,更新成本高且容易出錯。
  • 重複程式碼和資源浪費: 各專案可能重複實現相同功能,導致資源浪費和維護困難。
  • 協作效率低: 跨團隊協作難以同步進度,各自為政造成溝通和交付的瓶頸。

為了解決這些痛點,MonoRepo 應運而生。它將多個相關專案放在一個程式碼庫中,提供一致的依賴管理和工具支援,使團隊協作和程式碼複用變得更加高效。

MonoRepo 的優點:

  1. 統一依賴管理: 共享依賴和工具鏈,避免重複安裝和版本衝突。
  2. 跨專案協作: 專案間可以方便地引用和測試,減少交付時間。
  3. 原子化更改: 單次提交可以跨多個模組,確保更改的完整性和一致性。

然而,MonoRepo 也帶來了新的挑戰,比如如何高效地管理構建任務、最佳化效能、以及避免依賴關係複雜化。因此,適合 MonoRepo 的工具解決方案應運而生。

用一個場景來比對目前主流解決方案的差異

場景概述

假設我們有一個MonoRepo 工程。 整體工程結構如下。

my-monorepo/
├── package.json         # 根目錄
├── packages/
│   ├── lib-a/           # 公共庫,提供基礎功能
│   │   ├── src/
│   │   └── package.json
│   ├── app1/            # 應用1,依賴 lib-a
│   │   ├── src/
│   │   └── package.json
│   └── app2/            # 應用2,依賴 lib-a
│       ├── src/
│       └── package.json

如果,我們修改了 app1 和 app2 的依賴庫 lib-a 。 那麼,我們在執行 app1 或者 app2 的時候, 需要先執行 lib-a 在執行,具體工程。 如果,沒有變動,則直接執行app1 或 app2.

使用 PNPM workspaces

  1. 安裝依賴
pnpm install 

由於 pnpm 的特性, packages 下所有工程的共有依賴,透過符號連結連結到全域性快取。

  1. 執行構建任務
    pnpm recursive run build
  • 執行所有子專案的 build 指令碼,但無法自動跳過未修改的模組
  • 構建順序需人工保證正確,無法根據依賴關係自動調整。

優點:

簡單直接,適合小型專案。

使用全域性快取最佳化安裝速度和磁碟利用率。

子專案依賴不會汙染其他專案。

缺點:

無法基於任務依賴關係最佳化執行順序或跳過未修改模組。

只能適用於比較簡單,輕量級的專案。

使用 TurboRepo

TurboRepo 是由 Vercel 推出的現代化 MonoRepo 工具,專注於任務排程和構建最佳化。

  1. 配置工程

在專案根目錄建立 `turbo.json`:

{
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**"]
    }
  }
}
  1. 執行構建任務
turbo run build
  • TurboRepo 會自動分析依賴關係,確保 lib-a 構建在前。
  • 如果 lib-a 沒有修改,TurboRepo 會利用快取跳過構建,直接執行 app1app2 的任務。

優點:

  • 自動依賴管理,任務按順序執行。
  • 支援結果快取,大幅提升效率。

缺點:

  • 對大型專案的任務排程和視覺化支援較弱。

使用 Nx 解決

Nx 是一款功能強大的 MonoRepo 工具,提供了任務排程、依賴分析和視覺化支援。 前 MonoRepo 解決方案 lerna 的收購方。

安裝和初始化:

npx create-nx-workspace@latest my-monorepo

生成依賴圖:

nx graph

自動生成視覺化依賴圖,展示 lib-a 和應用之間的關係。

執行構建任務:

nx run-many --target=build --all
  • Nx 會根據依賴關係自動調整執行順序。
  • 支援本地和分散式快取,跳過未修改的模組任務。

優點:

  • 提供依賴視覺化工具,任務管理更加直觀。
  • 分散式快取適合團隊協作。

缺點:

  • 配置稍顯複雜,對上手要求較高。

使用 Rush 解決

Rush 是 Microsoft 為企業級專案設計的 MonoRepo 工具,專注於複雜依賴和構建管理。

初始化專案:

# 在根目錄下, 執行 rush init 初始化
rush init

定義構建流程:

rush.json 中配置依賴和任務:

{
  "projects": [
    {
      "packageName": "lib-a",
      "projectFolder": "packages/lib-a"
    },
    {
      "packageName": "app1",
      "projectFolder": "packages/app1",
      "dependencies": ["lib-a"]
    },
    {
      "packageName": "app2",
      "projectFolder": "packages/app2",
      "dependencies": ["lib-a"]
    }
  ]
}

執行構建任務:

rush build
  • Rush 會嚴格按照依賴順序執行任務,避免構建出錯。
  • 內建支援結果快取和高效的 CI/CD 整合。

優點:

  • 企業級功能強大,適合超大型團隊協作。

缺點:

  • 配置複雜,上手門檻高。

整體對比

工具依賴分析快取支援構建最佳化配置難度適用場景
pnpm workspaces本地快取無依賴最佳化簡單小型專案
TurboRepo自動本地快取增量構建簡單中小型專案
Nx強大本地/分散式快取視覺化依賴分析適中中大型專案
Rush強大分散式快取高效任務排程較高超大型專案,企業級團隊

總結

透過例子我們可以看出,不同的 MonoRepo 工具在依賴分析、任務排程和快取支援上的能力差異顯著:

  1. 輕量化選擇:

    • pnpm workspaces 適合簡單專案和個人開發者,易於上手但功能有限。
  2. 構建速度優先:

    • TurboRepo 在任務快取和增量構建方面表現出色,適合中小型專案。
  3. 團隊協作:

    • Nx 提供依賴圖和分散式快取功能,適合協作頻繁的中大型團隊。
  4. 企業級複雜場景:

    • Rush 是企業專案的最佳選擇,支援超大規模專案和嚴格的依賴管理。

最終,選擇工具時需要綜合考慮專案規模、團隊需求和未來擴充套件規劃,找到最適合的 MonoRepo 解決方案。希望本文的例項和對比能夠為你的選型提供啟發!

相關文章