iT邦幫忙

2025 iThome 鐵人賽

DAY 3
0

承先啟後的發展

2010 年的 Knockout.js 首度將 Observable / Computed 帶進前端,讓「資料自己開口,UI 跟着動」成為可行路徑。-> Knockout介紹

此後,前端的發展圍繞「到底是資料主動通知,還是框架主動打聽?」展開十多年的試驗。

從事後諸葛的角度來看,Knockout 確實是造成後來三強鼎立 (Angular, React, Vue) 的幕後推手,主要是思想層面上帶來的啟發,才激起後續不同解決方案的框架核心。

本文聚焦 UI 的 reactivity(與 FRP/Reactive Programming 有關聯、但不等同),並說明它如何影響之後的 Angular/React/Vue 以及 Signals 等技術走向。

Reactivity 的概念

讓「資料 → UI」的同步,從手動改 DOM,變成資料自己驅動畫面。

核心理念

  • 聲明式:你只描述『要呈現什麼』,框架負責『怎麼把變動反映到畫面』。
  • 依賴追蹤:當程式第一次讀取資料時,框架偷偷記下「誰用到誰」。
  • 變動傳播:資料改變後,沿著依賴關係把「失效訊號」傳給 UI,決定該更新哪裡。

為什麼需要?

  • 減少心智負擔:避免漏改或重複改動。
  • 效能優化:只改「真的要變」的部分,從整頁重新繪製演進到細顆粒節點更新。
  • 資料流向清楚:更容易除錯與測試。

兩大實作路線

誰先開口? 策略 典型做法 關鍵詞
框架主動詢問 Pull 迴圈比對/VDOM diff dirty-checking, diff
資料主動通知 Push watcher/signal observable, effect

多數現代框架其實是 Hybrid:由資料端 push 失效、在收斂時機 pull 需要的計算或 diff。

先快速對照,之後再細看每種做法如何平衡效能與心智負擔。

四大模型對照表

如果把這些試驗放到「Pull ↔ Push 光譜」上,就會看到一條清晰的演變脈絡。

https://ithelp.ithome.com.tw/upload/images/20250902/20129020vQ3OSK755H.png

模型對照表

模型 極簡更新流程 Push / Pull 落點 粒度 & 備註 代表框架
Dirty-checking $digest 逐一比對所有 $watch → 差異即時同步 純 Pull:框架主動「掃資料」 元件/表達式層級;效能隨監聽數量線性衰減 AngularJS 1.x (ryanclark.me)
Virtual-DOM diff setState Push 髒標記 → 批次 re-render → VDOM Pull diff → DOM patch Hybrid:Push 觸發+Pull 收斂 以「子樹」為單位;心智簡單但可能產生過度 render React, Preact, Vue 2 (Medium)
Watcher / Observable Graph setter Push 通知相關 Watcher → Watcher 只重算自己子樹 偏 Push 依賴由「getter 時自動追蹤」建立;粒度比 B 更細 Vue 2 Watcher, MobX (Vue.js)
Fine-grained Signals setter Push 失效 → 被讀取時 Pull 重算(lazy)→ 直接 patch DOM Runtime:Hybrid:Push 失效+Pull 計算;Compile-time:近純 Push 屬性/DOM 節點級;圖最細、無 VDOM Runtime:Solid.js, Angular Signals (DEV Community)|Compile-time:Svelte 5 Runes(Scalable Path)

Dirty-Checking

對應上表更新流程
https://ithelp.ithome.com.tw/upload/images/20250902/20129020jaT1gNL2Kb.png

Virtual-DOM diff

對應上表更新流程
https://ithelp.ithome.com.tw/upload/images/20250902/20129020EyuhcMXbG4.png

Watcher / Observable Graph

對應上表更新流程
https://ithelp.ithome.com.tw/upload/images/20250902/20129020Dv1KT4vrnV.png

Fine-grained Signals

對應上表更新流程
https://ithelp.ithome.com.tw/upload/images/20250902/20129020iZIiWIcr7x.png

深入比較重點

1. Pull vs. Push — 誰先開口?

  • Dirty-checking:純 pull(框架主動掃)。
  • Virtual DOM:push 髒標記 → pull diff(Hybrid)。
  • Watcher/Proxy/Signals(runtime):push 失效 → pull(lazy)求值(Hybrid)。
  • Compile-time signals(Svelte Runes):依賴編譯期決定,近純 push。

2. 依賴精度 — 通知到多細?

  • Virtual DOM:知道「哪棵 component 樹可能變」。
  • Proxy/Signals(runtime):可精準到「哪個 Memo/DOM 節點」改變。
  • Compile-time:直接輸出對應 DOM 操作,覆蓋率最高。

3. 調度 (Scheduling) — 何時真正動手?

  • React 與 Solid:透過 batch / microtask,在同一 tick 合併多次修改。
  • Vue 3:以 job-queue 方式分批。
  • Dirty-checking:則同步跑迴圈,成本與監聽數量相關。

4. 心智模型 — 寫程式的感受

  • Signals/MobX:像「直接改變值」→ 框架負責把變更傳到用到它的地方。
  • Virtual DOM:接受「render ≠ paint」的宣告式心智模型。
  • Compile-time signals:語法更貼近直覺;靠編譯器與 IDE 提示維持可預測性。

結語

Reactivity 的進化是一條不斷調和「推」與「拉」的歷史。

  • 我們從 Dirty-checking 的純 Pull,走到 Virtual DOM 的 Hybrid 再到 fine-grained 的圖形化依賴
  • runtime signals 多採「push 失效 + pull(lazy)求值」
  • 而 compile-time signals 則把多數工作前移到編譯期,接近純 push

到這裡,你已掌握了四大模型在「誰先開口、開口後影響多大」上的差異。
但還有一個關鍵問題尚未揭開:即使在最細顆粒度的系統裡,為什麼仍需要「Pull」?

下一篇,讓我們來詳細理解,什麼是 Pull-based & Push-based?


上一篇
Signal 的核心概念
系列文
Reactivity 小技巧大變革:掌握 Signals 就這麼簡單!3
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言