iT邦幫忙

2025 iThome 鐵人賽

DAY 28
1
Modern Web

Angular 進階實務 30天系列 第 28

Day 28:淺談 Monorepo

  • 分享至 

  • xImage
  •  

前言

當專案規模開始變大了之後,在比較有條理的工作流程中,通常會開始建立符合自己專案特色的 UI 元件庫,這裡簡要地談一下 Monorepo ,之後帶到 library 跟部署上 Verdaccio ,幫這個30天的系列收個尾。另外也附上 Angular 版本使用的差異表。

兩種方式比較

特點 Standalone 元件 SharedModule
Angular 版本 14+ 推薦 傳統方式,所有版本支援
引入方式 直接 import 元件 import 整個 Module
靈活性 高,按需引入 中等,需要整個模組
檔案大小 更小,Tree-shaking 友善 較大,可能包含未使用元件
學習曲線 簡單,直觀 需要了解 NgModule 概念
依賴管理 元件自行管理依賴 模組統一管理依賴
測試 單元測試較簡單 需要設定 TestingModule
程式碼結構 分散式,各元件獨立 集中式,統一管理
遷移成本 新專案建議使用 現有專案可繼續使用
效能 較佳,按需載入 一般,整包載入

什麼是 Monorepo?

Monorepo 本質上就是「一個倉庫管理多個專案」的概念。

如果把字拆開來看:

  • Mono:源自希臘語 μόνος (monos),意思是「單一、唯一」。
  • Repository:倉庫,通常是指版本控制的儲存空間。

所以 Monorepo 就是「單一倉庫」的意思。直白來說,就是把多個專案放進同一個版本控制倉庫(Git、SVN 都可以),由此達到統一管理的效果。


為什麼需要 Monorepo?

剛開始接觸時,我簡單理解成「在一個 Git repository 裡面放多個專案」。但實際上,Monorepo 更注重的是協作與共用的價值。

以公司內部系統為例,常常會有前台、後台系統,甚至是 API 或 mobile app,雖然面向的使用者不同,但背後可能都是由同一個單位提出需求。這樣的情境下,專案之間有許多共通點:

  • 設計規格相似(UI 元件、色彩風格)
  • 環境設定相似(API domain、認證機制)
  • 資料模型相似(Model、Enum、Interface)

如果分散在多個 repository 管理,不但需要重複維護,追蹤起來也容易混亂。反之,若集中在同一個 Monorepo:

  1. 清楚追蹤:一次 commit 就能記錄前後台的改動,方便回顧歷史。
  2. 共用程式碼:model、enum、interface、甚至 UI component 都能直接共用。
  3. 一致性:Lint、測試、CI/CD pipeline 都能統一設定,降低分歧。

實際案例:Angular Monorepo

Angular 官方就推薦使用 Angular Workspace 來實現 Monorepo,搭配 Angular CLI 就能輕鬆建立多個專案。

ng new company-workspace --create-application false
cd company-workspace
ng generate application frontend
ng generate application backend
ng generate library shared
  • frontend:前台專案
  • backend:後台專案
  • shared:共用程式碼(例如 model、enum、UI 元件)

這樣一來,不同應用程式可以引用 shared library,而不是各自重複寫。


範例:共用按鈕樣式

假設我們需要在前後台都使用同樣的 UI 元件。就可以在sharedModule處理

由於我們前面示範時做了卡片跟計數器(counter),所以以下提供按鈕的例子,大家可以自己試著加入shared裡面共用。

shared library 建立元件

ng generate component shared/button

然後在 button.component.ts 中:

import { Component, Input } from '@angular/core';

@Component({
  selector: 'app-shared-button',
  template: `<button [ngClass]="style">{{ label }}</button>`,
  styles: [`
    button { padding: 8px 16px; border-radius: 8px; }
    .primary { background: #1976d2; color: white; }
    .secondary { background: #e0e0e0; color: #333; }
  `]
})
export class SharedButtonComponent {
  @Input() label = '按鈕';
  @Input() style: 'primary' | 'secondary' = 'primary';
}

建立 shared.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { SharedButtonComponent } from './button/button.component';

@NgModule({
  declarations: [
    SharedButtonComponent
  ],
  imports: [
    CommonModule
  ],
  exports: [
    SharedButtonComponent
  ]
})
export class SharedModule { }

同樣的 card 也可以用類似方式建立。之後在 frontendbackend 專案中,只要 import SharedModule 就能直接使用。


部署原則

其實目前接觸到的monorepo,在寫法上都沒有什麼困難,最困難的反而是在部署安裝上

如果你只要提供編譯後的靜態資料給後端或是相關的部署人員,你只要寫個script,編譯前後台的專案就好, shared 的部分 angular 會在編譯的時候自動處理進去(大多數情況下)。

# 簡單的部署 script 範例
npm run build admin -- --base-href="/admin/"
npm run build user -- --base-href="/user/"

但如果你是要寫自動化部署,那就會有一些坑可以踩,由於平台眾多,請大家在部署的時候先確保達成以下幾點:

路徑和基礎設定

  • 設定 baseHref:每個應用要對應自己的部署子路徑
  • 使用相對資源路徑:避免 /assets/... 造成部署後讀不到

建置產物處理

  • 規劃 outputPath:各應用的建置檔案要有獨立目錄
"outputPath": "dist/admin",  // 而不是共用 dist/
  • 開啟 outputHashing=all:避免檔案命名衝突並利於快取

依賴和模組

  • 使用單一套件管理(workspace):確保版本一致,避免重複打包

網頁伺服器配置

  • 設定 SPA fallback:不論是自己設定,或是平台提供,請確保路由導向回 index.html
// GitHub Pages 404.html 範例
if (location.pathname.startsWith('/admin/')) {
  location.replace('/admin/');
}
  • 確認 MIME 類型正確:特別是 .js.mjs.css

環境配置

  • 使用 fileReplacements:production 環境變數正確替換
  • API 端點對應正確環境

💡 90% 的部署問題都出在路徑設定,先確保 baseHref 和資源路徑正確,其他問題就好解決了。


結語

Monorepo 優勢總結

  • 程式碼共用:共用元件、樣式、介面定義
  • 一致性:確保前後台視覺和功能一致性
  • 維護性:統一管理,易於版本控制和 bug 修復
  • 開發效率:避免重複開發相同功能
  • 部署便利:可以統一建置和部署流程

透過 Monorepo 架構,我們成功建立了一個可重用的 UI 函式庫,讓前後台都能使用相同的元件,大幅提升開發效率和維護便利性。

缺點 也要注意:

  • 倉庫體積會快速變大
  • CI/CD pipeline 可能變慢,也有一定學習門檻(需要搭配工具做優化)
  • 團隊習慣要改變(所有人都在同一倉庫協作)

總而言之 Monorepo 提供了前後台共用程式碼的極佳方式,也能讓專案結構更有條理。只要搭配合適的工具與部署流程,Monorepo 能大幅提升團隊的維護效率。


上一篇
Day27:如何寫出可維護的元件?
下一篇
Day 29:Angular Library – 建立與共用元件
系列文
Angular 進階實務 30天31
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言