詳解vue3 響應式的實現原理

核心設計思想

除瞭組件化,Vue.js 另一個核心設計思想就是響應式。它的本質是當數據變化後會自動執行某個函數,映射到組件的實現就是,當數據變化後,會自動觸發組件的重新渲染。響應式是 Vue.js 組件化更新渲染的一個核心機制。

Vue.js 2.x 響應式

我們先來回顧一下 Vue.js 2.x 響應式實現的部分: 它在內部通過 Object.defineProperty API 劫持數據的變化,在數據被訪問的時候收集依賴,然後在數據被修改的時候通知依賴更新。

在 Vue.js 2.x 中,Watcher 就是依賴,有專門針對組件渲染的 render watcher。這裡有兩個流程,首先是依賴收集流程,組件在 render 的時候會訪問模板中的數據,觸發 getter 把 render watcher 作為依賴收集,並和數據建立聯系;然後是派發通知流程,當我對這些數據修改的時候,會觸發 setter,通知 render watcher 更新,進而觸發瞭組件的重新渲染。

Object.defineProperty API 的一些缺點:

不能監聽對象屬性新增和刪除;
初始化階段遞歸執行 Object.defineProperty 帶來的性能負擔。

在 Vue.js 2.x 中,data 中定義的數據,Vue.js 內部在組件初始化的過程中會把它變成響應式,這是一個相對黑盒的過程,用戶通常不會感知到。

Vue.js 3.x 響應式

Vue.js 3.0 為瞭解決 Object.defineProperty 的這些缺陷,使用 Proxy API 重寫瞭響應式部分,並獨立維護和發佈整個 reactivity 庫。

也就是在 Vue.js 3.0 中,是用 reactive 這個有魔力的函數,把數據變成瞭響應式。

reactive 內部通過 createReactiveObject 函數把 target 變成瞭一個響應式對象,這個函數主要做瞭以下幾件事情:

1、函數首先判斷 target 是不是數組或者對象類型,如果不是則直接返回。所以**原始數據 target 必須是對象或者數組**。
2、如果對一個已經是響應式的對象再次執行 reactive,還應該返回這個響應式對象。
3、如果對同一個原始數據多次執行 reactive ,那麼會返回相同的響應式對象。
4、使用 canObserve 函數對 target 對象做一進步限制。
5、通過 Proxy API 劫持 target 對象,把它變成響應式。
6、給原始數據打個標識。

響應式的實現方式無非就是劫持數據,Vue.js 3.0 的 reactive API 就是通過 Proxy 劫持數據,而且由於 Proxy 劫持的是整個對象,所以我們可以檢測到任何對對象的修改,彌補瞭 Object.defineProperty API 的不足。

依賴收集:get 函數

依賴收集發生在數據訪問的階段

get 函數主要做瞭四件事情:

1、對特殊的 key 做瞭代理
2、通過 Reflect.get 方法求值
3、執行 track 函數收集依賴(最核心)
4、對計算的值 res 進行判斷,如果它也是數組或對象,則遞歸執行 reactive 把 res 變成響應式對象。

Object.defineProperty 是在初始化階段,即定義劫持對象的時候就已經遞歸執行瞭,而 Proxy 是在對象屬性被訪問的時候才遞歸執行下一步 reactive,這其實是一種延時定義子對象響應式的實現,在性能上會有較大的提升

收集的依賴就是數據變化後執行的副作用函數

每次 track ,就是把當前激活的副作用函數 activeEffect 作為依賴,然後收集到 target 相關的 depsMap 對應 key 下的依賴集合 dep 中。

派發通知:set 函數

派發通知發生在數據更新的階段

set 函數主要就做兩件事情:

1、通過 Reflect.set 求值
2、通過 trigger 函數派發通知(最核心),並依據 key 是否存在於 target 上來確定通知類型,即新增還是修改。

trigger 函數主要做瞭四件事情:

1、通過 targetMap 拿到 target 對應的依賴集合 depsMap;
2、創建運行的 effects 集合;
3、根據 key 從 depsMap 中找到對應的 effect 添加到 effects 集合;
4、遍歷 effects 執行相關的副作用函數。

每次 trigger 函數就是根據 target 和 key ,從 targetMap 中找到相關的所有副作用函數遍歷執行一遍。

依賴收集和派發通知的過程中都提到副作用函數,依賴收集過程中我們把 activeEffect(當前激活副作用函數)作為依賴收集。

總結

其實 Vue.js 3.0 在響應式的實現思路和 Vue.js 2.x 差別並不大,主要就是 劫持數據的方式改成用 Proxy 實現 , 以及收集的依賴由 watcher 實例變成瞭組件副作用渲染函數 。

源碼參考

由於源碼太多,本文就不展示,可直接去: github.com/vuejs/core

packages/reactivity/src/baseHandlers.ts
packages/reactivity/src/effect.ts
packages/reactivity/src/reactive.ts
packages/reactivity/src/baseHandlers.ts
packages/reactivity/src/ref.ts

到此這篇關於vue3 響應式的實現原理的文章就介紹到這瞭,更多相關vue3 響應式原理內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: