Skip to content

元件設計

與 blueprint 的關係:這些軸線就是 blueprint config 裡的 componentShape 區塊。
每條軸線標的初篩規則(max-paramsmax-statementsmax-lines)會進 lint config,當「該檢查哪裡」的入口;
至於結論,轉譯進手冊與 AI agent 守則。

核心只有一句:blueprint 不管你元件「怎麼寫」,只管它還好不好懂、好不好改、好不好重用。
所以它不看程式風格,而是用一組彼此正交的軸線來衡量 ——
這是「一組」、不是「一條流程」:每條各自獨立判斷,不能因為「第三條過了」就說「第一條自動成立」;
編號只是拿來指認、不代表順序,小改動也不必每條都跑一遍。

這套看法跟框架無關,Vue、React 都通 —— Vue 的 composable = React 的 hook,只是名字不同。

這節的檢查幾乎全落在 ◐ 跟 ○ 級 ——
元件設計本來就是判斷題,這正是信念第六條在講的事。

1. 所有權反轉 —— 誰需要衍生狀態,誰自己持有

父元件不要先算好一堆衍生資料、再一層層 props 傳下去;
子元件自己知道怎麼拿,就讓它自己匯入 composable 或 hook 去算。
實測:一個元件的 props 從 17 個降到 7 個。

2. 介面收縮 —— 輸入收窄、輸出精簡

三種手法:把管很多件事的單元拆開;
把「帶著不變量、卻各存一份」的平行原始狀態,收成一份建好模的狀態;
把對稱的成對輸出,收成同一種型別的物件。
數量跟大小都只是弱訊號 —— 狀態有沒有好好建模才是 review 真正在看的。
初篩規則:max-params

3. 單一職責拆分 —— 依責任邊界切分,而非依大小

一句話的檢驗法:不用「和」就講不清它在做什麼,那就該拆。
把內容各自歸還到它該待的地方(「解散」)也是一種拆。
例外:共用又必須同步的可寫狀態 —— 硬拆只會製造同步 bug。
初篩規則:max-statements

4. 協調層外殼 —— 頁面只做協調

頁面只做這些:解析路由與 id、撐起載入狀態的外殼、備好共享資料來源、串起跨子元件的生命週期 ——
不替個別子元件去算衍生值。
實測:一個明細頁從 6,666 行砍到 552 行。
初篩規則:max-lines(套在 pages 層)。

5. 可寫狀態的作用域 —— 放在「寫入者與讀取者」的最近共同祖先

真的跨邊界共享的可寫狀態,才往上搬;
要跨路由活著的,放到網址查詢參數或 store。
「以後搞不好會共享」是過度設計 —— 真的要共享了再上提。

6. 生命週期內化 —— 生命週期是職責的一部分,就內建進去

呼叫端拿到的,該是一個「已經啟動、也會自己收拾」的單元,
而不是一堆要自己接到 onMounted / useEffect 上的零件。
實測:19 個匯出項,收成呼叫端的一行。

7. 純函式不等於 composable —— 純函式別跟響應式單元混在一起

「一個匯出函式」不等於「一個檔案」:
責任是在函式層級切,檔案則是快到 max-lines 才切。
對外要給的是這個單元做出來的決定,不是一堆原料。


lint 跟 review 怎麼分工(每條軸線都適用)
一個單元可以行數少、複雜度低、被依賴少 —— 每項量測都綠 ——
卻還是個原始狀態的垃圾場,或把衍生值存成一個可變的 ref;這些量測工具全看不到。
lint 警告是 review 的起點,不是結論。