👉什麼時候該抽取元件?如何設計一個好的元件?怎樣避免過度工程化?
元件化不僅僅是代碼複用,更是關於關注點分離、可測試性和可維護性。一個設計良好的元件應該具備:
前端開發最直觀的判斷方式就是視覺相似性。當你看到兩個或多個UI區塊具有相同的功能和外觀時,這就是抽取元件的明確信號。
// ❌ 重複的代碼片段
// 頁面A
<div class="card">
<div class="card-header">使用者資訊</div>
<div class="card-body">
<span>{{userA.name}}</span>
<nz-tag [nzColor]="userA.status === 'active' ? 'green' : 'red'">
{{userA.status}}
</nz-tag>
</div>
</div>
// 頁面B
<div class="card">
<div class="card-header">管理員資訊</div>
<div class="card-body">
<span>{{adminUser.name}}</span>
<nz-tag [nzColor]="adminUser.status === 'active' ? 'green' : 'red'">
{{adminUser.status}}
</nz-tag>
</div>
</div>
特別將表格拉出來講,是因為表格看起來平易近人,又長得很像,好像可以共用元件一樣,但其實他要達成共用的難度最高,其他樣式幾乎長得像就可以抽出來了,但表格不是。
許多開發者看到相似的表格就想將 th 、 td 的內容做成共用元件,但實際上這往往是個災難:
最重要的判斷點是:表格的核心服務對象。
只要使用單位不同,甚至只是商品項目(EX不同險種保單)不一樣,每個欄位就會有不同的客製化條件。
第二個判斷點是:一格塞很多不一樣的資料欄位,如果是的話建議不要用。
從上圖可以發現使用者表格的 使用者資訊 需要 頭像+姓名+email 的複合顯示,
訂單表格的 商品資訊 需要 商品清單+數量 的複合顯示,這種複合顯示的資料型態基本上不會一樣,
而他們的狀態、時間可能會使用不同的管道(pipe)來調整資料,
所以會變成在創造一個新的規則但是彈性很低。
// ❌ 看似通用,實則複雜
@Component({
selector: 'app-generic-table',
template: `
<nz-table [nzData]="data">
<thead>
<tr>
<th *ngFor="let col of columns">{{col.title}}</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of data">
<td *ngFor="let col of columns">
<!-- 這裡會變得非常複雜 -->
<div [ngSwitch]="col.type">
<span *ngSwitchCase="'text'">{{item[col.key]}}</span>
<nz-tag *ngSwitchCase="'tag'">{{item[col.key]}}</nz-tag>
<button *ngSwitchCase="'button'" nz-button>操作</button>
<!-- 更多客製化需求... -->
</div>
</td>
</tr>
</tbody>
</nz-table>
`
})
export class GenericTableComponent {
@Input() data: any[] = [];
@Input() columns: TableColumn[] = [];
}
現實情況:表格中的每個儲存格往往需要高度客製化的顯示邏輯,包括:
適合做成元件的情況:
不適合過早抽取的情況:
當單個元件檔案超過500行時,就應該考慮拆分:
// ❌ 過於龐大的元件
@Component({
selector: 'app-user-management',
templateUrl: './user-management.component.html', // 300+ 行
styleUrls: ['./user-management.component.scss'] // 200+ 行
})
export class UserManagementComponent {
// 500+ 行的邏輯代碼
// 使用者列表邏輯
// 使用者編輯邏輯
// 權限管理邏輯
// 審核流程邏輯
// ...
}
// ✅ 拆分後的清晰結構
@Component({
selector: 'app-user-management',
template: `
<app-user-list
[users]="users"
(userSelect)="onUserSelect($event)">
</app-user-list>
<app-user-editor
[selectedUser]="selectedUser"
(userSave)="onUserSave($event)">
</app-user-editor>
<app-permission-panel
[user]="selectedUser"
(permissionChange)="onPermissionChange($event)">
</app-permission-panel>
`
})
export class UserManagementComponent {
// 只負責協調各子元件的互動
}
元件化的價值在於提升可維護性與複用性,掌握「UI結構是否一致」、「邏輯是否固定」、「複用頻率是否足夠」這幾個判斷點,才能讓元件發揮真正的價值,而不是變成新的負擔。
關鍵判斷法則:
✅ 3次以上使用 + UI結構高度一致 = 值得抽取
⚠️ 表格元件需謹慎:服務對象不同或複合顯示需求高 = 建議獨立開發
🚨 500行代碼警戒線:超過此數值就該考慮拆分
當你開始為了讓元件「更通用」而加第二個、第三個特殊判斷時,通常代表這個元件已經開始走歪了。這時候停下來,重新評估一下會比硬撐下去要好。
元件化沒有標準答案,多試幾次就會找到適合自己專案的節奏了。