👉什麼時候該抽取元件?如何設計一個好的元件?怎樣避免過度工程化?
元件化不僅僅是代碼複用,更是關於關注點分離、可測試性和可維護性。一個設計良好的元件應該具備:
前端開發最直觀的判斷方式就是視覺相似性。當你看到兩個或多個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行代碼警戒線:超過此數值就該考慮拆分
當你開始為了讓元件「更通用」而加第二個、第三個特殊判斷時,通常代表這個元件已經跟初始設計不合了。這時候停下來,重新評估一下會比硬撐下去要好。
元件化沒有標準答案,多試幾次就會找到適合自己專案的節奏了。