當專案規模開始變大了之後,在比較有條理的工作流程中,通常會開始建立符合自己專案特色的 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 能大幅提升團隊的維護效率。