Signals:前端響應式的新潮流

5 分鐘

1. 響應式模型的演進

前端開發的本質是「將狀態映射為 UI」。多年來,我們見證了響應式模型的不斷演進:從手動 DOM 操作,到 MVC/MVVM 模式,再到由 React 推廣的虛擬 DOM(Virtual DOM)。VDOM 透過在元件層級進行狀態變更的批次處理和 Diffing,極大地簡化了 UI 開發。

然而,VDOM 並非銀彈。其元件層級的重新渲染和 Diffing 開銷,在某些高度動態的情境下會成為效能瓶頸。近年來,一種更古老但經過現代編譯器最佳化的範式——細粒度響應式(Fine-Grained Reactivity)——重新回到舞臺中央,而其核心載體正是 Signals

2. 什麼是 Signals?

Signal 是一種響應式原語(Reactive Primitive),它封裝了一個值,並能在該值變化時自動通知其所有相依項目。與 VDOM 框架在元件層級追蹤變化不同,Signal 在資料讀取的層面自動建立相依關係圖。

當一個 Signal 的值被更新時,只有直接或間接相依於該 Signal 的計算(Memo)或副作用(Effect)會被重新執行,從而實現「外科手術式」的精確 DOM 更新,完全繞過了 VDOM Diffing 的需要。

其核心通常由三個原語構成:

  • createSignal(value): 建立一個響應式的 state 單元。
  • createEffect(() => {}): 建立一個自動追蹤相依關係並響應變更的副作用。
  • createMemo(() => {}): 建立一個衍生的、帶快取的響應式計算。
// SolidJS 语法示例
const [count, setCount] = createSignal(0);

const doubleCount = createMemo(() => count() * 2);

createEffect(() => {
  console.log(`The double count is: ${doubleCount()}`);
});

setCount(1); // 这将触发 memo 重新计算,并触发 effect 重新执行

一個關鍵的心智模型是:在基於 Signal 的框架(如 SolidJS)中,元件函式本身只在初始化時執行一次。後續的更新完全由響應式系統驅動,而不是透過元件的重新渲染。

3. 生態系統的廣泛採用

Signal 並非全新概念,其思想可以追溯到 Knockout.js 等早期框架。但藉助現代 JavaScript 編譯器,它煥發了新的生機,並被各大框架廣泛採用:

  • SolidJS & Qwik: 完全基於 Signal 建構,是該範式的標竿實作。
  • Preact: 透過 @preact/signals 套件引入了官方支援,並可以與現有的 Preact/React 專案整合。
  • Vue: 其 Composition API 中的 refcomputed 本質上就是 Signal 的一種實作。
  • Svelte 5: 即將到來的「Runes」更新,是 Svelte 團隊對響應式模型的一次重構,其核心正是借鑑並實作了 Signal 範式。
  • Angular: 近期版本中也引入了 Signals 作為新的響應式原語。

這種跨框架的趨同,證明了 Signal 作為一種高效、可預測的響應式模型的價值。

4. 優勢與權衡

優勢:

  1. 卓越的效能: 透過避免 VDOM 和元件層級的重新渲染,在效能基準測試中通常表現優異。
  2. 可預測性: 狀態更新的流向清晰,易於理解和除錯。
  3. 記憶體占用小: 無需維護整個元件樹的虛擬表示。

權衡:

  1. 心智模型轉變: 對於習慣了 React 渲染週期的開發者來說,需要適應「元件只執行一次」的新模型。
  2. 生態整合: 雖然自身效能強大,但與龐大的 React VDOM 生態(例如,某些 UI 函式庫)的深度整合可能需要額外的適配工作。

結論

Signals 的復興標誌著前端狀態管理範式的一次重要演進。它將開發者的注意力從「元件如何渲染」拉回到了「狀態如何流動」這一更本質的問題上。透過與編譯器的深度結合,Signal 在提供優秀開發體驗的同時,也為 Web 應用程式的效能邊界帶來了新的可能性。