前端框架的未來:useSignal--
Signal(信號)是一種存儲應用狀態的形式,類似於 React 中的 useState()
。但是,有一些關鍵性差異使 Signal 更具優勢。Vue、Preact、Solid 和 Qwik 等流行 JavaScript 框架都支持 Signal。
下面就來看看 Signal 都有哪些優勢,爲什麼說它是前端框架的未來!
Signal 是什麼?
Signal 和 State 之間的主要區別在於 Signal 返回一個 getter 和一個 setter,而非響應式系統返回一個值和一個 setter。
注意: 有些響應式系統同時返回一個 getter/setter,有些則返回兩個單獨的引用,但思想是一樣的。
Signal 的優勢
State 混淆了兩個獨立的概念:
-
StateReference:對狀態的引用。
-
StateValue:存儲在狀態引用 / 存儲中的實際值。
那爲什麼返回一個 getter 比返回一個值更好呢?因爲通過返回 getter,可以將狀態引用的傳遞與狀態值的讀取分開。
下面以這段 SolidJS 代碼爲例:
-
createSignal()
:分配 StateStorage 並將其初始化爲 0。 -
getCount
:可以傳遞的對狀態的引用。 -
getCount()
:獲取狀態值。
上面解釋了 Signal 和普通 State 的不同。那 Signal 到底有什麼優勢呢?Signal 是響應式的,這意味着它需要跟蹤誰對狀態感興趣(訂閱者),如果狀態發生變化,則通知訂閱者狀態變化。
爲了具有響應式,Signal 必須要收集誰對它的值感興趣。它通過觀察在什麼情況下調用狀態 getter 來獲取此信息。通過從 getter 中獲取值,告訴 Signal 該位置對該值感興趣。如果值發生變化,則需要調用 getter 創建一個訂閱。
這就是爲什麼傳遞狀態 getter 而不是狀態值很重要的原因。狀態值的傳遞不會向 Signal 提供有關實際使用該值的位置的任何信息。這就是爲什麼區分狀態引用和狀態值在 Signal 中如此重要。
爲了進行對比,這裏是 Qwik 中的相同示例。請注意,(getter/setter) 已被替換爲具有 .value
屬性(表示 getter/setter)的單個對象。雖然語法不同,但內部的工作原理是一樣的。
count.value
只能被文本節點訪問。因此,它知道如果 count
的值發生變化,就只需要更新文本節點而無需更新其他地方。
useState() 的缺點
下面來看看在 React 中是如何使用 useState()
的以及它的缺點。
useState()
會返回一個狀態值。這意味着 useState()
不知道組件或應用內部如何使用狀態值。所以,一旦通過調用 setCount()
通知 React 狀態更改,React 就不知道頁面的哪一部分發生了更改,因此必須重新渲染整個組件,這在計算上是很昂貴的。
useRef() 不渲染
React 的 useRef()
類似於 useSignal()
,但它不會導致頁面重新渲染。下面的例子看起來與 useSignal()
非常相似,但它不起作用。
useRef()
的使用與 useSignal()
完全一樣,用於傳遞對狀態的引用而不是狀態本身。但是,useRef()
缺少了訂閱跟蹤和通知。
在基於 Signal 的框架中,useSignal()
和 useRef()
是一樣的。useSignal()
可以執行 useRef()
加上訂閱跟蹤的功能,這樣就進一步簡化了框架的 API。
內置的 useMemo()
Signal 很小需要進行記憶,因爲它需要做的工作很少。
下面來看一個包含兩個計數器和兩個子組件的例子:
# 初始渲染輸出
<Counter/>
<Display count={0}/>
<Display count={0}/>
# 單擊時的渲染
(空白)
實際上,我們無法在 React 中實現相同的效果,因爲至少會有一個組件需要重新渲染。那麼下面就來看看如何在 React 中記憶組件以最小化重新渲染的次數。
# 初始渲染輸出
<Counter/>
<Display count={0}/>
<Display count={0}/>
# 單擊時的渲染
<Counter/>
<Display count={1}/>
如果沒有記憶,我們會看到:
# 初始渲染輸出
<Counter/>
<Display count={0}/>
<Display count={0}/>
# 單擊時的渲染
<Counter/>
<Display count={1}/>
<Display count={0}/>
這比 Signal 需要做的工作多得多。所以,這就是爲什麼 Signal 的工作就像把所有事情都記下來了,而不必由我們自己來記憶。
舉個例子
下面就來看一個實現購物車的例子(React):
cart
以及兩個子組件:
-
Main 組件:通過多層組件傳遞 setCart 函數,直到它到達購買按鈕。
-
NavBar 組件:通過多層組件傳遞購物車狀態,直到它到達渲染購物車的組件。
這裏的問題就是每次點擊購買按鈕時,大部分組件樹都必須重新渲染。這會導致類似於的輸出:
# 點擊購買按鈕時
<App/>
<Main/>
<Product/>
<NavBar/>
<Cart/>
如果使用記憶,那麼就可以避免 Main 組件重新渲染,而只有 NavBar 組件重新渲染:
# 點擊購買按鈕時
<App/>
<NavBar/>
<Cart/>
如果使用 Signal,輸出是這樣的:
# 點擊購買按鈕時
<Cart/>
這大大減少了需要執行的代碼量。
總結
Signal 是在應用中存儲狀態的一種方式,類似於 React 中的 useState()
。兩者的關鍵區別在於,Signal 返回一個 getter 和一個 setter,而非響應式系統只返回一個值和一個 setter。
Signal 是響應式的,這意味着它需要跟蹤誰對狀態感興趣並通知訂閱者狀態更改。這是通過觀察調用狀態 getter 的上下文來實現的,它創建了一個訂閱。
相比之下,React 中的 useState()
僅返回狀態值,這意味着它不知道如何使用狀態值,並且必須重新渲染整個組件樹以響應狀態變化。
所以說 Signal 是前端框架的未來!
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/1VWULD5jaUEicMtAEa0MWw