做前端做到一段時間後,很容易遇到這種情況:
原本 Component
↓
看起來不符合需求
↓
開始想辦法繞
↓
越改越複雜
↓
最後才發現:
其實原本就有一個參數可以做到
這種時候最想做的通常不是 Debug。
而是:
去牆角安靜三分鐘。
我自己以前很容易有一個習慣:
看到元件不符合需求,就先想「我要怎麼改掉它」。
但後來慢慢發現,很多時候真正該先問的是:
這個元件原本到底支援什麼?
假設專案裡原本已經有一個共用的數字輸入元件。
概念上像:
<AmountInput />
但某次需求希望:
可以顯示特定格式
可以保留原本數字行為
又要符合新的顯示需求
第一眼看:
這個 Component 好像做不到。
所以很容易開始:
換成普通 Input
↓
自己處理 value
↓
自己 parse
↓
自己 format
↓
自己處理空值
↓
自己處理小數
↓
自己處理表單
結果原本只是一個欄位,
開始長出一大堆額外邏輯。
原本數字元件可能已經偷偷幫我們處理:
number type
format
parser
precision
min / max
Form integration
validation
空值
如果直接換成:
<Input />
畫面看起來可能暫時一樣。
但資料層其實已經變了。
例如原本:
100
是:
Number
換成普通 Input 後:
"100"
可能變成:
String
這件事如果沒有注意,
後面送 API 時就可能再出現型別問題。
這點我後來很有感。
UI Component 不是只有:
長什麼樣子
它還可能負責:
輸入型別
資料格式
驗證
Form 行為
事件
所以把:
<InputNumber />
改成:
<Input />
並不只是換 UI。
有可能連資料 contract 都一起改了。
如果是:
Ant Design
Ant Design Pro
公司共用元件
我現在會先做一件事:
找它支援哪些 Props
例如:
<Component
formatter={...}
parser={...}
precision={...}
fieldProps={...}
/>
也可能是公司自己包裝的:
<AmountField
showSymbol={false}
/>
重點不在參數名稱。
而是先確認:
原本 Component 有沒有已經提供這個能力?
例如:
我要把這個數字欄位改成某種樣子。
腦袋會直接進:
要不要換 Input?
要不要自己寫 formatter?
要不要另外存 State?
現在我會先換一個順序:
① 現在使用什麼 Component?
② 它從哪裡 import?
③ 是套件的還是專案共用的?
④ 它有哪些 Props?
⑤ 專案其他地方有沒有類似用法?
⑥ 現有能力真的做不到,再考慮重做。
這個順序其實省很多時間。
例如:
import AmountInput from "@/components/AmountInput"
這跟:
import { InputNumber } from "antd"
意義差很多。
第一種:
專案自己包裝的 Component
第二種:
第三方套件 Component
如果是公司自己包的:
AmountInput
我會先進去看:
它到底包了什麼?
有時候會發現:
return (
<InputNumber
formatter={...}
parser={...}
{...props}
/>
)
這就代表:
它本身可能已經允許外面傳更多設定進來。
這個方法對我非常有用。
假設現在看到:
<AmountInput />
不知道某個需求怎麼做,
可以直接搜尋:
AmountInput
看看其他頁面是不是有人寫:
<AmountInput
someProp={...}
/>
有時候文件還沒看到答案,
專案裡其實已經有人做過。
這種 Existing Pattern 非常值得先參考。
因為它除了告訴我:
這個 Component 能不能做到
還告訴我:
這個專案習慣怎麼做到。
以前我可能會覺得:
工程師是不是應該自己想出來?
但實際專案裡,
保持一致性本身就是很重要的事情。
如果:
同一個需求
專案其他地方已經有:
既有 Component
既有 Pattern
既有 Utility
重新自己發明一套,
反而會增加:
維護成本
行為差異
重複邏輯
所以搜尋 Existing Pattern 不是偷懶。
而是在問:
這個 Codebase 已經怎麼解這件事?
假設用了:
<InputNumber />
不要只看:
InputNumber 是數字輸入框
真正有用的是去看:
Props
例如:
formatter
parser
precision
stringMode
controls
min
max
因為很多需求其實不是:
Component 做不到。
而是:
我還不知道它有這個入口。
因為一個共用 Component 不可能把每一個專案需求都寫死。
它通常會設計成:
預設行為
+
可配置參數
例如:
<MyComponent
editable
disabled
loading
formatter={...}
/>
所以 Props 本質上可以理解成:
Component 留給外部調整行為的入口。
前面我們學 Props 時:
Parent
↓
Props
↓
Child
比較像是在理解:
資料怎麼傳。
但在 Component Library 裡,
Props 還有另一個很重要的角色:
Configuration。
例如:
<Input
disabled
/>
disabled 不只是資料。
它是在告訴 Component:
這次請用「不可編輯」的模式運作。
第一種:
Data Props
例如:
value={user.name}
代表:
我要顯示什麼資料。
第二種:
Behavior / Configuration Props
例如:
disabled
loading
multiple
formatter
代表:
Component 要怎麼運作。
這個區分讓我讀第三方元件時清楚很多。
因為它通常已經處理過:
型別
邊界值
Form 整合
錯誤狀態
既有樣式
專案一致性
如果只是少一個參數,
例如:
<MyInput
someOption
/>
那通常比:
把整個元件換掉
+
重新實作原本能力
風險低很多。
如果確認:
現有 Component 本身不適合需求
Props 無法支援
硬改會讓 API 很奇怪
需求已經偏離原元件責任
那重做或抽新的 Component 完全合理。
真正的差別是:
我理解它做不到
↓
所以替換
而不是:
我不知道它能不能做到
↓
直接替換
不知道元件能力
↓
重做
比較像:
在資訊不足時做決策。
確認現有能力
↓
評估成本
↓
決定沿用或替換
這才比較像:
有意識地做工程選擇。
看到需求時,不要馬上問:
我要怎麼寫?
而是先問:
專案裡已經有什麼?
這個 Component 原本解決什麼?
它有哪些擴充入口?
真的需要新增一套嗎?
很多時候,
找到正確的 existing API,
比自己多寫 50 行 Code 還重要。
現在有 AI 幫忙,
只要說:
幫我做一個符合這個需求的 Input。
很快就能產生一套新的實作。
問題是 AI 不一定知道:
你的專案裡已經有一個 Component
你的套件已經有一個 Prop
你們團隊已經有既有 Pattern
如果沒有先理解 Codebase,
很容易變成:
需求
↓
AI 產新 Code
↓
功能能跑
↓
但專案裡多了一套重複邏輯
所以 AI 越方便,
反而越需要問:
這段東西真的需要重新寫嗎?
需求來了
↓
確認目前使用的 Component
↓
看 Import
↓
看 Component Source / Documentation
↓
搜尋 Existing Usage
↓
確認 Props / API
↓
能設定解決?
├─ Yes
│ ↓
│ 優先沿用
│
└─ No
↓
評估 Wrapper / Extend / Replace
這條流程其實比:
直接開始 Coding
更重要。
看到現有 Component 不符合需求時,
先不要急著:
拆掉
重寫
換成最原始 Input
先問:
我是真的確認它做不到,還是我只是還沒看懂它?
因為成熟的開發不只是:
能不能把功能做出來。
還包括:
能不能利用已經存在的能力,用更小的修改完成需求。
我現在越來越覺得,
「少寫一段不必要的新 Code」
有時候比:
「成功寫出一段新 Code」
更像是在進步。
下一篇可以接一個跟今天很像、但更進一步的工程判斷:
套件明明可以硬改到符合需求,但為什麼最後反而選擇換一種元件?