Compound Components
What — 父元件管理共享狀態,子元件透過 React Context 隱性存取;消費者透過組合子元件來組裝 UI,不需要傳遞大量 props。
Why — Prop drilling(多層 props 傳遞)讓元件 API 臃腫、難以擴展;Compound Components 把「狀態」留在父元件,「佈局控制」交給消費者。
When — 適用:複雜 UI 元件有多個協作子部件:Select、Accordion、Tabs、Modal、Combobox。不適用:簡單的 stateless 展示元件、只有一個子部件的元件(context 的 overhead 不值得)。
Where — UI 元件庫層、設計系統層;任何需要暴露組合 API 而非 prop API 的元件。
Who — Radix UI https://github.com/radix-ui/primitives(業界最成熟的 headless compound component library);Headless UI https://github.com/tailwindlabs/headlessui(Tailwind Labs 出品);Reach UI https://github.com/reach/reach-ui;Ark UI https://github.com/chakra-ui/ark
實踐 — 用 React.Children.map 找子元件(脆弱)已過時,現代做法全用 Context;要提供好的 TypeScript 型別讓 IDE 補全子元件;compound component 的 displayName 要設好(方便 React DevTools debug);Radix Primitive 的源碼是學習 compound component 最好的一手資料。
自訂 Hook 組合(Custom Hook Composition)
What — 把有狀態的邏輯(state + effects + callbacks)抽取成獨立 hook;多個 hook 組合成更高階的 hook,形成邏輯層次。
Why — HOC(Higher-Order Component)和 render props 讓元件樹膨脹、wrapper hell;自訂 hook 讓邏輯複用不污染元件層次,且天然可測試(hook 可以獨立 test,不需要 render)。
When — 適用:相同的業務邏輯在多個元件中重複(fetch 資料、表單驗證、鍵盤快捷鍵、local storage sync)。不適用:邏輯只有一個元件使用(直接寫在元件裡更清楚);需要在 class component 中使用(hooks 只在 function component)。
Where — 業務邏輯層、資料獲取層;以及任何需要跨元件共享邏輯但不需要共享 UI 的場景。
Who — TanStack Query https://github.com/TanStack/query(`useQuery`, useMutation,hook 組合的巔峰範例);SWR https://github.com/vercel/swr(Vercel,`useSWR` hook 設計);React Hook Form https://github.com/react-hook-form/react-hook-form(`useForm` + useWatch + useController 組合)
實踐 — hook 命名一定要 use 前綴(React lint rule);每個 hook 做一件事(單一職責)——過度組合成「上帝 hook」是常見反模式;自訂 hook 的依賴陣列要正確——比元件更難 debug(useCallback / useMemo 的 memoization 邊界);用 Vitest/React Testing Library 的 renderHook 獨立測試 hook 邏輯。
State Machines(狀態機)
What — 把 UI 的狀態用有限狀態機(FSM)或狀態圖(statechart)明確建模:有哪些狀態、哪些轉換(transition)、轉換的守衛(guard)與行動(action)。
Why — 用 boolean flags 管理 UI 狀態(isLoading, isError, isSuccess, isRetrying)會產生「不可能狀態」(impossible states):isLoading=true 同時 isSuccess=true;隨 feature 增長,flag 組合爆炸。狀態機讓合法狀態有限、轉換明確、不可能狀態在設計時就被排除。
When — 適用:多步驟流程(checkout, onboarding)、非同步操作的 loading/error/success 週期、modal 的 open/closing/closed 動畫狀態。不適用:只有 true/false 的簡單 toggle、純展示型元件(無互動狀態)。
Where — 複雜互動元件的狀態管理層;業務流程編排層(wizard、multi-step form)。
Who — XState https://github.com/statelyai/xstate(JavaScript 最成熟的狀態機/statechart library,有視覺化工具 Stately Studio);Zag.js https://github.com/chakra-ui/zag(Chakra UI 團隊,用狀態機驅動 headless UI components);Robot https://github.com/matthewp/robot
實踐 — 從畫狀態圖開始,再寫 code(Stately Studio 可視覺化);XState v5 改用 setup() 語法,舊 v4 的 machine 定義方式已不同;Actor model 和狀態機天然結合——XState actor 是個很好的架構單元;不要把所有 UI 都狀態機化,ROI 在於「狀態複雜、轉換多」的場景。
Signals(訊號)
What — 細粒度的響應式原語(reactive primitive):讀取 signal 自動追蹤依賴,signal 值改變時只重新執行實際依賴到它的計算或副作用,不需要 VDOM diff 整棵樹。
Why — React 的渲染模型是「從組件根往下 re-render,VDOM diff 後 patch DOM」——對於高頻更新(滑鼠位置、遊戲狀態、即時資料)過於粗糙。Signals 讓更新路徑短路到最小。
When — 適用:高頻率 UI 更新(動畫、即時輸入、遊戲 loop)、大型元件樹中只需要部分更新、需要精確控制 reactivity 的場景。不適用:已用 React + React Compiler(自動 memoization)且性能夠用的場景,切換成 signals 的遷移成本不值得。
Where — 高性能互動層;框架的響應式核心(Solid.js、Angular、Vue 3 內建);或作為 React 生態的漸進增強(Preact Signals)。
Who — Solid.js https://github.com/solidjs/solid(signals 作為核心,無 VDOM,性能 benchmark 長期領先);Preact Signals https://github.com/preactjs/signals(可以在 React 裡使用的 signal 實作);Angular Signals(Angular 16+ 內建,https://angular.dev/guide/signals);Vue 3 Reactivity system(ref/computed 的底層就是 signals 概念,https://github.com/vuejs/core);Qwik https://github.com/QwikDev/qwik
實踐 — Signal 的計算(computed / derived)是惰性求值(lazy)+ 自動追蹤——比 useEffect + useState 組合更安全(不會漏依賴);Preact Signals 在 React 生態可以漸進引入;不要把 signal 當成全局 store 使用(沒有 DevTools 支援、難以 debug 大型 signal graph);React Compiler(2024)試圖讓 React 的細粒度更新接近 signals 的效果,但兩者的哲學根本不同。
Islands Architecture(孤島架構)
What — 頁面以靜態 HTML 為基礎,只有真正需要互動的「島嶼」才包含 JavaScript 並獨立 hydrate;各島嶼彼此隔離,不共享 JS runtime。
Why — SPA 把整個應用的 JS 打包傳到客戶端,大量 JavaScript 解析導致 TTI(Time to Interactive)緩慢;大多數內容型網站 80% 以上的 DOM 是靜態的,沒必要 hydrate 整棵樹。
When — 適用:內容型網站(部落格、文件、電商型錄頁)以靜態內容為主、互動區域少且分散。不適用:高度互動的 app(Google Docs、Figma 等)、島嶼之間需要大量共享狀態(Islands 的天然隔離在這裡反而成為障礙)。
Where — 靜態網站產生層(SSG);內容平台的頁面渲染策略;性能優先的行銷頁面。
Who — Astro https://github.com/withastro/astro(Islands Architecture 的代表實作,支援 React/Vue/Svelte 混用);Fresh https://github.com/denoland/fresh(Deno 官方,Preact 驅動);Marko https://github.com/marko-js/marko(eBay 出品,更早的 islands 先驅);11ty + is-land https://github.com/11ty/is-land
實踐 — island 的邊界劃定是設計重點——太細碎(每個按鈕一個 island)比太粗糙(大段互動塊一個 island)更難維護;Astro 的 client:load / client:visible / client:idle 指令是控制 hydration 優先序的好設計;跨 island 狀態共享可以用 nanostores https://github.com/nanostores/nanostores 或 URL 狀態。
React Server Components(RSC)
What — 在伺服器上渲染、永遠不會被傳送到客戶端的 React 元件;可以直接存取資料庫、檔案系統;元件的 import(含依賴)不計入客戶端 bundle。
Why — 傳統的 SSR 仍然把元件 code 傳給客戶端 hydrate;RSC 讓「不需要互動的元件」完全不占 bundle 空間,且可以在服務端直接做資料查詢(消除 client-side waterfall)。
When — 適用:資料獲取密集的頁面、使用大型依賴(markdown parser、syntax highlighter、重型計算)但展示靜態結果的元件、需要直接存取後端資源的佈局元件。不適用:需要 useState / useEffect / 瀏覽器 API 的互動元件(這些必須是 Client Components)。
Where — Next.js App Router 的 layout 層、資料獲取層;伺服器端資料密集型的頁面骨架。
Who — React https://github.com/facebook/react(RFC: https://github.com/reactjs/rfcs/blob/main/text/0188-server-components.md);Next.js https://github.com/vercel/next.js(App Router,RSC 的主要生產環境採用者);Waku https://github.com/dai-shi/waku(輕量 RSC framework,學習 RSC 底層的好資料)
實踐 — Server Component 和 Client Component 的邊界是 "use client" 指令——一旦標記 "use client",整棵子樹都是 client;Server Component 可以 import Client Component,但反過來不行;RSC 最大的心智負擔是「哪些 API 在哪個環境可用」——建議用 eslint-plugin-react-server-components 靜態檢查;RSC payload 透過 wire format(React Fiber serialization)傳輸,debug 要用 Next.js DevTools。
Optimistic UI
What — 用戶操作觸發後,UI 立即呈現預期結果(不等待伺服器回應);伺服器確認後 commit,伺服器拒絕則 rollback 並顯示錯誤。
Why — 網路延遲讓 UI 感覺遲鈍;對於低失敗率的操作(按讚、移動卡片、已讀標記),等待伺服器回應只是浪費用戶感知到的時間。
When — 適用:用戶操作成功率高(>95%)且操作可逆(失敗後 rollback 不複雜):like/unlike、drag-and-drop 排序、mark as read、form inline edit。不適用:高失敗率操作(付款)、操作不可逆(刪除重要資料)、需要伺服器生成 ID 再呈現(rollback 邏輯複雜)。
Where — 高互動性的 CRUD 介面;社群功能(反應、留言、追蹤);任何操作延遲感知影響核心 UX 的場景。
Who — TanStack Query https://github.com/TanStack/query(`onMutate` / onError / onSettled 的 optimistic update pattern 是業界範本);SWR optimistic UI pattern;Linear(知名 issue tracker,拖曳排序和即時更新廣為稱道,Engineering Blog 有公開細節)
實踐 — rollback 邏輯要在 onMutate 時快照舊狀態(TanStack Query 提供 context 機制);同時發出多個 optimistic mutation 時要排隊或加樂觀鎖 version 號;UI 更新和 cache 更新要同步做,否則重新 fetch 時閃爍;告知用戶「正在更新中」的視覺提示(spinner、disabled)有時比 optimistic 更誠實——根據操作的心理重量選擇。
Micro-frontends
What — 把前端應用按業務域或團隊拆分成獨立可部署的子應用;在執行期或建置期組合成一個整體(shell/container app + micro-apps)。
Why — 大型前端應用隨團隊擴張成為開發瓶頸:一個 PR 影響全局、技術選型被綁定、部署窗口互相等待;Micro-frontends 讓各團隊獨立迭代。
When — 適用:多個團隊(通常 3+ 個)同時開發一個前端應用、各部分有不同的部署節奏與技術選型需求。不適用:單一小團隊(協作成本反超組合收益)、產品尚在探索期(架構邊界未定)、用戶體驗一致性要求極高且現有協調機制不夠(跨 micro-app 的樣式/動畫/狀態一致性是長期痛點)。
Where — 大型企業應用的架構層;多團隊平台產品的前端組織策略。
Who — Module Federation (Webpack 5) https://github.com/module-federation/core(最廣泛採用的執行期組合方案,webpack 5 內建);single-spa https://github.com/single-spa/single-spa(前 MFE router,支援多框架混用);Bit https://github.com/teambit/bit(component-level sharing);Zack Jackson 的 Module Federation 部落格是最好的技術文件之一
實踐 — 共享依賴(React、設計系統)要用 Module Federation shared 機制避免多版本衝突;跨 micro-app 通訊用 Custom Events 或 shared EventBus(不要用 global state);樣式隔離用 CSS Modules 或 Shadow DOM;Micro-frontends 的 CI/CD pipeline 複雜度遠超單一應用——需要 contract testing;不要因為「技術潮流」就做 MFE,先問「獨立部署的需求是否真實存在」。
與既有專欄的關係
前端如何從後端領域模型推導 UI 結構,見 前端從領域模型推導(event-driven 專欄)。
本文的 pattern 與 AI coding 的交集:Compound Components 的邊界設計、RSC 的 server/client 分界——這些對 AI 生成正確 code 的要求更高(邊界模糊時 AI 會混用),見 Engineering AI Coding Methodology。
🔍 待解問題 / 持續追蹤
- React Compiler 是否會讓 Signals 的性能優勢收窄?React 團隊的公開 benchmark 有限,需要持續觀察。
- Micro-frontends 的 DX(開發體驗)—— Module Federation 的 hot reload 跨 micro-app 場景仍有 friction,是否有更好的工具鏈?
- RSC 的 server/client 邊界在 AI coding 工具(GitHub Copilot、Claude Code)中的正確率——AI 是否理解 “use client” 的語意?尚無公開數據。
- Islands Architecture 與 RSC 的取捨邊界:兩者都追求減少客戶端 JS,但模型不同——何時各自更合適?
🕒 更新紀錄
- 2026-07-03 — 建立。涵蓋 Compound Components、自訂 Hook 組合、State Machines、Signals、Islands Architecture、RSC、Optimistic UI、Micro-frontends 八個前端 pattern。