當專案規模開始變大了之後,在比較有條理的工作流程中,通常會開始建立符合自己專案特色的 UI 元件庫,這裡簡要地談一下 Monorepo ,之後帶到 library 跟部署上 Verdaccio ,幫這個30天的系列收個尾。另外也附上 Angular 版本使用的差異表。
| 特點 | Standalone 元件 | SharedModule | 
|---|---|---|
| Angular 版本 | 14+ 推薦 | 傳統方式,所有版本支援 | 
| 引入方式 | 直接 import 元件 | import 整個 Module | 
| 靈活性 | 高,按需引入 | 中等,需要整個模組 | 
| 檔案大小 | 更小,Tree-shaking 友善 | 較大,可能包含未使用元件 | 
| 學習曲線 | 簡單,直觀 | 需要了解 NgModule 概念 | 
| 依賴管理 | 元件自行管理依賴 | 模組統一管理依賴 | 
| 測試 | 單元測試較簡單 | 需要設定 TestingModule | 
| 程式碼結構 | 分散式,各元件獨立 | 集中式,統一管理 | 
| 遷移成本 | 新專案建議使用 | 現有專案可繼續使用 | 
| 效能 | 較佳,按需載入 | 一般,整包載入 | 
Monorepo 本質上就是「一個倉庫管理多個專案」的概念。
如果把字拆開來看:
所以 Monorepo 就是「單一倉庫」的意思。直白來說,就是把多個專案放進同一個版本控制倉庫(Git、SVN 都可以),由此達到統一管理的效果。
剛開始接觸時,我簡單理解成「在一個 Git repository 裡面放多個專案」。但實際上,Monorepo 更注重的是協作與共用的價值。
以公司內部系統為例,常常會有前台、後台系統,甚至是 API 或 mobile app,雖然面向的使用者不同,但背後可能都是由同一個單位提出需求。這樣的情境下,專案之間有許多共通點:
如果分散在多個 repository 管理,不但需要重複維護,追蹤起來也容易混亂。反之,若集中在同一個 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 也可以用類似方式建立。之後在 frontend 與 backend 專案中,只要 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:避免檔案命名衝突並利於快取index.html
// GitHub Pages 404.html 範例
if (location.pathname.startsWith('/admin/')) {
  location.replace('/admin/');
}
.js、.mjs、.css
fileReplacements:production 環境變數正確替換💡 90% 的部署問題都出在路徑設定,先確保 baseHref 和資源路徑正確,其他問題就好解決了。
Monorepo 優勢總結
透過 Monorepo 架構,我們成功建立了一個可重用的 UI 函式庫,讓前後台都能使用相同的元件,大幅提升開發效率和維護便利性。
但 缺點 也要注意:
總而言之 Monorepo 提供了前後台共用程式碼的極佳方式,也能讓專案結構更有條理。只要搭配合適的工具與部署流程,Monorepo 能大幅提升團隊的維護效率。