詳解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!
推薦閱讀:
- Vue3 Reactive響應式原理邏輯詳解
- vue3中的響應式原理-effect
- vue3.0響應式函數原理詳細
- 源碼分析Vue3響應式核心之effect
- vue響應式Object代理對象的修改和刪除屬性