我們常在 Signal / Proxy / Virtual DOM 之間搖擺:哪個更快?哪個更好維護?
如果把口號 UI = f(state) 當作主線,答案其實會自己冒出來。
這篇先把這句話的來龍去脈講成一個小故事,接著拆解三種模型的差異,再附最小對照碼與選型清單,讓你在專案裡有所本,也更容易說服團隊。
1997 年的 ICFP 研討會上,Conal Elliott 發表〈Functional Reactive Animation〉,主張畫面其實是「時間 → 圖形
」的純函數。當下只在學界泛起漣漪,卻種下「介面可以用數學映射描述」的種子。
十多年後,Evan Czaplicki 把這顆種子帶進瀏覽器,推出 Elm 0.1:view : Model → Html msg
。他把狀態稱為 Model,把 UI 寫成 view 函數,讓前端工程師第一次看到「UI 其實等於 f(state)」的可運行範例。
到了 2013 年 JSConf US,Jordan Walke 用更貼近 JavaScript 的方式包裝成 React,並把口訣寫在投影片上:UI = f(state)
──只要把最新 state 丟進 render,框架就負責把 DOM 修成正確的樣子。
這句話離開論文與 Haskell,成為日常工程溝通的共同語言。
也因此,後續才分化出三條優化之路:把 f
拆得更細 的 Signal、用 Proxy 免設定的 Vue reactivity,以及依賴 Diff 的 Virtual DOM。
UI = f(state) 不是口號,而是從 1990 年代函數式研究,到 2010 年代資料驅動 UI 思潮,最後在主流 JavaScript 生態落地的縮影。
理解這條演進,你就能看懂今日三種路徑為何各走各的實作,卻都在努力把同一個 f
變得更好寫、更高效。
接下來,每種模型都用同一組問題對齊比較:
batch()
、微任務 queue,衍生值(memo)可 lazy 計算形成 Hybrid push/pull。Proxy(get/set)
攔截任意屬性讀寫,屬性級依賴自動收集;深層物件需沿著 getter 鏈註冊依賴。f(state)
的快照,再與上一版 Diff。setState
使 component 重新 render;React 以 Fiber Scheduler 做優先級與時間切片,提升互動流暢度。// Signal
const [count, setCount] = createSignal(0)
createEffect(() => {
console.log('count changed ->', count())
})
setCount(1) // 只觸發用到 count 的 effect/綁定
// Proxy Reactive
const state = reactive({ count: 0 })
watchEffect(() => {
console.log('count changed ->', state.count)
})
state.count++ // 觸發一次,深層物件會沿 getter 鏈註冊依賴
// Virtual DOM
function Counter() {
const [count, setCount] = useState(0)
console.log('Counter render') // 每次 set 都重新執行,再交給 Diff
return <button onClick={() => setCount(c => c + 1)}>{count}</button>
}
你會發現差別不在 JSX 長相,而在狀態改變後「誰」決定該不該重算:
- Signal/Proxy 由「資料」決定(push)
- VDOM 由「比對」決定(pull)
需求情境 | 建議技術 | 原因 |
---|---|---|
互動高度即時(遊戲座標、Canvas 畫筆) | Signal | micro-update 超低 diff 成本 |
巨型表單/深層 JSON 編輯器 | Proxy Reactive | 不必大量手動宣告 signal;語法貼近原生物件 |
團隊已深耕 React、生態依賴重 | Virtual DOM | DX、工具鏈、人才供應,改動成本最低 |
混合應用:大部分 React,局部需極速 | React + Signals as 外掛 | 只在熱點用 signal,其他維持 VDOM |
理解了三者在「依賴 → 調度 → DOM」的成本分佈,你就能在不同專案裡找到最貼切的 reactivity 策略。
下一篇,讓我們來聚焦於這系列的主軸 Signal 的運作原理,來探討如何實踐這套體系的運作。
FRP 原典 – Functional Reactive Animation, ICFP 1997
Elm Guide – “The Elm Architecture” 章(view : Model -> Html)
JSConf US 2013 React Talk(Jordan Walke)
UI is a function of state(2015 blog)
The Two Reacts(Dan Abramov,2024)