三個容易混淆的前端框架概念
大家好,我卡頌。
有3個容易混淆的前端框架概念:
- 響應式更新
- 單向數據流
- 雙向數據綁定
在繼續閱讀本文前,讀者可以思考下是否明確知道三者的含義。
這三者之所以容易混淆,是因為他們雖然同屬前端框架范疇內的概念,但又不是同一抽象層級的概念,不好直接比較。
本文會從3個抽象層級入手講解這三者的區別。
響應式更新
?「響應式更新」也叫「細粒度更新」。同時,最近前端圈比較火的??Signal?
?這一概念描述的也是「響應式更新」。
籠統的講,「響應式更新」描述的是「狀態與UI之間的關系」,即「狀態變化如何映射到UI變化」。
考慮如下例子(例子來自what are signals[1]一文):
在TodoApp組件中,定義了兩個狀態:
- 待辦事項todos
- 是否展示完成的事項showCompleted
以及根據上述狀態派生出的狀態filteredTodos?。最終,返回<TodoList/>組件。
如果todos?狀態變化,UI該如何變化?即「我們該如何知道狀態變化的影響范圍」?這時,有兩個思路:
- 推(push)
- 拉(pull)
推的原理
我們可以從變化的狀態(例子中為todos)出發,根據狀態的派生關系,一路推下去。
圖片來自what are signals一文
在例子中:
- todos變化
- filteredTodos?由todos派生而來,變化傳導到他這里
- <TodoList/>?組件依賴了filteredTodos,變化傳導到他這里
- 確定了todos變化的最終影響范圍后,更新對應UI
這就建立了「狀態與UI之間的關系」。
除了「推」之外,還有一種被稱為「拉」的方式。
拉的原理
同樣的例子,我們也能建立「狀態與可能的UI變化的關系」,再反過來推導??UI?
?變化的范圍。
圖片來自what are signals一文
在例子中:
- todos變化。
- 可能有UI變化(因為建立了「狀態與可能的UI變化的關系」)。
- UI?與<TodoList/>組件相關,判斷他是否變化。
- <TodoList/>?組件依賴filteredTodos,filteredTodos由todos派生而來,所以filteredTodos是變化的。
- 既然filteredTodos變化了,那么<TodoList/>組件可能變化。
- 計算變化的影響范圍,更新UI。
在主流框架中,React?的更新以「推」為主,Vue?、Preact?、Solid.js等更多框架使用「拉」的方式。
本文聊的「響應式更新」就是「拉」這種方式的一種實現。
單向數據流
我們可以發現,不管是「推」還是「拉」,他們都需要計算變化的影響范圍,即「一個狀態變化后,究竟有多少組件會受影響」。
那么,從框架作者的角度出發,是希望增加一些約束,來減少「計算影響范圍」這一過程的復雜度。
同樣,從框架使用者的角度出發,也希望增加一些約束,當「計算影響范圍」出??bug?
?后,更容易排查問題。
這就有了「單向數據流」。
「單向數據流」是一條約定,他規定了「當狀態變化后,變化產生的影響只會從上往下傳遞」。
考慮如下例子:
<Parent/>?組件的狀態num?作為props?傳給<Child/>?組件,再作為props?傳給<GrandChild/>組件,整個過程只能自上而下。
「單向數據流」并不是實現前端框架必須遵循的原則,他的存在主要是為了減少開發者的心智負擔,讓「狀態變化后,計算影響范圍」這一過程更可控。
雙向數據綁定
當本文開篇聊「響應式更新」時,討論的是「狀態與UI的關系」,這是將框架作為一個整體來討論,抽象層級比較高。
當我們繼續聊到「單向數據流」時,討論的是「狀態變化的影響范圍在組件間單向擴散」,這是「組件與組件之間的關系」,抽象層級下降了一級。
接下來我們要討論的「雙向數據綁定」,討論的是單個組件內發生的事。
「雙向數據綁定」是「狀態+改變狀態后觸發的回調」相結合的語法糖。
這里不討論框架語境下「語法糖」一詞是否完全準確
比較知名的「雙向數據綁定」實現,比如??Vue?
?中的??v-model?
?語法:
相當于如下狀態+事件回調的組合:
實際上早期React中也有類似實現,名叫LinkedStateMixin[2],只是早已被廢棄
總結
我們可以用一張圖概括本文介紹的3個概念之間的關系:
概括起來主要是兩點:
- 他們都是前端框架范疇內的概念
- 他們屬于不同抽象層級的概念
其中:
- 「雙向數據綁定」描述的是「組件內邏輯與視圖的關系」
- 「單向數據流」描述的是「組件之間的關系」
- 「響應式更新」描述的是「狀態與UI之間的關系」
參考資料
[1]what are signals:https://signia.tldraw.dev/docs/what-are-signals。
[2]LinkedStateMixin:https://reactjs.org/docs/two-way-binding-helpers.html。