從小學數學聊前端框架設計

你們好,我是卡·小學生·頌。前端

很開心還有不到10天小學就放暑假了,到時候打農藥被人噴了就能說:編程

「我媽只准我放假玩,手有點兒生」數組

說回前端。前端框架

其實前端框架是個很簡單的東西,大部分框架的工做原理能夠用一個小學知識解釋清楚。markdown

本文會從這個知識入手,逐步談到前端框架更新粒度背後的權衡。框架

看完本文,不但能收穫一個審視不一樣框架的視角,也能瞭解React Hooks產生的緣由。函數

可不要再瞧不起咱們小學生哦!學習

小學生.gif

自變量、因變量與響應式更新

這個小學知識就是:自變量因變量ui

對於以下等式:this

2x + 1 = y
複製代碼

x自變量y的值受x影響,是因變量

在不少框架與狀態管理庫中,一樣存在自變量與因變量。

Vue3中的自變量:

const x = value(1);
// 取值
console.log(x.value);
// 賦值
x.value = 2;
複製代碼

MobX的自變量:

const x = observable({data: 1});
// 取值
console.log(x.data);
// 賦值
x.data = 2;
複製代碼

React的自變量:

const [x, setX] = useState(1);
// 取值
console.log(x);
// 賦值
setX(2);
複製代碼

這些框架(或庫)的自變量由getter(取值)與setter(賦值)兩部分構成。

有了自變量,固然也有因變量。咱們能夠按有無反作用區分。

Vue的因變量:

// 無反作用
// 賦值
const y = computed(() => x.value * 2 + 1);
// 取值
console.log(y.value);

// 有反作用
watch(() => document.title = x.value);
複製代碼

MobX的因變量:

// 無反作用
const y = computed(() => x.data * 2 + 1);
console.log(y.get());

// 有反作用
autorun(() => document.title = x.data);
複製代碼

React的因變量:

// 無反作用
const y = useMemo(() => x * 2 + 1, [x]);
console.log(y);

// 有反作用
useEffect(() => document.title = x, [x]);
複製代碼

有了自變量因變量,再配合描述視圖的方式,就能描述組件UI

好比在React中,經過JSX描述視圖:

const [x, setX] = useState(21);
const y = useMemo(() => x * 2 + 1, [x]);

return <p>個人戰績是 0/{x}/{y}</p>;
複製代碼

再加上各類使用戶能夠操縱自變量的事件,如給p增長onClick

<p onClick={() => setX(x + 1)}>個人戰績是 0/{x}/{y}</p>;
複製代碼

最後再加上少許輔助的鉤子函數,如:組件發生錯誤時的鉤子函數

就組成一個功能完備的組件。

這就是全部細粒度更新框架的底層共通之處:

經過事件驅動自變量改變,進而最終驅動視圖(或反作用)變化

面向對象之痛

在咱們初學編程時,都學過一個概念 —— 面向對象(下文簡稱OO),也很容易接受一個設定 —— OO能夠提升可讀性且易維護。

緣由是:OO是對現實世界的模擬。好比:

能夠繼承哺乳動物的屬性,這就是個OO模型

然而實際操做起來卻事與願違。

回想你學習ReactClass組件時,在OO簡單的表象背後,是複雜的生命週期概念,隨便問你幾個問題:

  • shouldComponentUpdate的原理是?

  • componentWillReceiveProps何時觸發?

  • getDerivedStateFromPropsderivedState是什麼意思?

好在React團隊也意識到這個問題,並着手作出改變。

改變的結果,就是Hooks

使用Hooks的函數組件與Class組件最大的區別是:

從擁有生命週期的實例自變量、因變量與視圖的映射關係轉變

若是接受了這個設定,想一想如今主流的學習Hooks的方式(甚至React官網也是如此),竟然是:

用生命週期函數來類比Hooks執行時機

是否是還蠻搞笑的。

戴着腳鐐跳舞的React

理想是美好的,但是React底層並非一個細粒度框架。

這就形成在實現自變量因變量時會有諸多限制,好比:

Hooks調用順序不能變(不能寫在條件語句中)

再好比,不知道你發現一個細節沒:

React實現因變量時須要第二個參數顯式指出本身的自變量是誰。好比:

const y = useMemo(() => x * 2 + 1, [x]);
useEffect(() => document.title = x, [x]);
複製代碼

反觀其餘框架(或庫)就不須要。好比Vue

const y = computed(() => x.value * 2 + 1);
watch(() => document.title = x.value);
複製代碼

爲何會有這些限制呢?我用兩個比喻來解釋。

剛纔聊到,在細粒度框架中,交互流程能夠歸納爲:

用戶觸發事件 -> 自變量改變 -> 因變量改變 -> 映射到視圖變化
複製代碼

就像一個畫家在畫畫,畫的每一筆對應一個自變量變化,再最終對應畫面變化。

React的更新機制大致歸納爲:

用戶觸發事件 -> 觸發更新 -> 虛擬DOM全量對比 -> 將對比結果映射爲視圖操做
複製代碼

就像一我的拿相機拍一張照片,再拿這張照片和上次拍的照片找不一樣,最後把不一樣的地方更新了。

當調用this.setState(或useStatesetter),並非畫下一筆,而是按下快門。

怎麼能從一張新照片中發現自變量呢?因此React只能拿新老照片對比。

淨整些奇怪的

社區早有人意識到這個問題,因而Mobx誕生了。他帶來純粹的細粒度更新能力。

然而,這個能力是創建在React更新機制之上,就像:

一個畫家,拿畫筆在畫板上一戳,戳到什麼呢?戳到相機快門了。

咔嚓拍了張照片,畫家再拿照片與老照片對比,將對比結果再畫到畫布上。

因此有人吐槽:用React+Mobx爲啥不直接用Vue

然而,Vue自己也依賴虛擬DOM,粒度並非最細的。

更準確的說法應該是:用React+Mobx爲啥不直接用SolidJS

吶,過幾天咱們來聊聊純粹的細粒度更新框架(SolidJS)的實現原理吧。

相關文章
相關標籤/搜索