詳解vue3?響應(yīng)式的實(shí)現(xiàn)原理
核心設(shè)計(jì)思想
除了組件化,Vue.js 另一個(gè)核心設(shè)計(jì)思想就是響應(yīng)式。它的本質(zhì)是當(dāng)數(shù)據(jù)變化后會(huì)自動(dòng)執(zhí)行某個(gè)函數(shù),映射到組件的實(shí)現(xiàn)就是,當(dāng)數(shù)據(jù)變化后,會(huì)自動(dòng)觸發(fā)組件的重新渲染。響應(yīng)式是 Vue.js 組件化更新渲染的一個(gè)核心機(jī)制。
Vue.js 2.x 響應(yīng)式
我們先來回顧一下 Vue.js 2.x 響應(yīng)式實(shí)現(xiàn)的部分: 它在內(nèi)部通過 Object.defineProperty API 劫持?jǐn)?shù)據(jù)的變化,在數(shù)據(jù)被訪問的時(shí)候收集依賴,然后在數(shù)據(jù)被修改的時(shí)候通知依賴更新。
在 Vue.js 2.x 中,Watcher 就是依賴,有專門針對(duì)組件渲染的 render watcher。這里有兩個(gè)流程,首先是依賴收集流程,組件在 render 的時(shí)候會(huì)訪問模板中的數(shù)據(jù),觸發(fā) getter 把 render watcher 作為依賴收集,并和數(shù)據(jù)建立聯(lián)系;然后是派發(fā)通知流程,當(dāng)我對(duì)這些數(shù)據(jù)修改的時(shí)候,會(huì)觸發(fā) setter,通知 render watcher 更新,進(jìn)而觸發(fā)了組件的重新渲染。
Object.defineProperty API 的一些缺點(diǎn):
不能監(jiān)聽對(duì)象屬性新增和刪除; 初始化階段遞歸執(zhí)行 Object.defineProperty 帶來的性能負(fù)擔(dān)。
在 Vue.js 2.x 中,data 中定義的數(shù)據(jù),Vue.js 內(nèi)部在組件初始化的過程中會(huì)把它變成響應(yīng)式,這是一個(gè)相對(duì)黑盒的過程,用戶通常不會(huì)感知到。
Vue.js 3.x 響應(yīng)式
Vue.js 3.0 為了解決 Object.defineProperty 的這些缺陷,使用 Proxy API 重寫了響應(yīng)式部分,并獨(dú)立維護(hù)和發(fā)布整個(gè) reactivity 庫(kù)。
也就是在 Vue.js 3.0 中,是用 reactive 這個(gè)有魔力的函數(shù),把數(shù)據(jù)變成了響應(yīng)式。
reactive 內(nèi)部通過 createReactiveObject 函數(shù)把 target 變成了一個(gè)響應(yīng)式對(duì)象,這個(gè)函數(shù)主要做了以下幾件事情:
1、函數(shù)首先判斷 target 是不是數(shù)組或者對(duì)象類型,如果不是則直接返回。所以**原始數(shù)據(jù) target 必須是對(duì)象或者數(shù)組**。 2、如果對(duì)一個(gè)已經(jīng)是響應(yīng)式的對(duì)象再次執(zhí)行 reactive,還應(yīng)該返回這個(gè)響應(yīng)式對(duì)象。 3、如果對(duì)同一個(gè)原始數(shù)據(jù)多次執(zhí)行 reactive ,那么會(huì)返回相同的響應(yīng)式對(duì)象。 4、使用 canObserve 函數(shù)對(duì) target 對(duì)象做一進(jìn)步限制。 5、通過 Proxy API 劫持 target 對(duì)象,把它變成響應(yīng)式。 6、給原始數(shù)據(jù)打個(gè)標(biāo)識(shí)。
響應(yīng)式的實(shí)現(xiàn)方式無非就是劫持?jǐn)?shù)據(jù),Vue.js 3.0 的 reactive API 就是通過 Proxy 劫持?jǐn)?shù)據(jù),而且由于 Proxy 劫持的是整個(gè)對(duì)象,所以我們可以檢測(cè)到任何對(duì)對(duì)象的修改,彌補(bǔ)了 Object.defineProperty API 的不足。
依賴收集:get 函數(shù)
依賴收集發(fā)生在數(shù)據(jù)訪問的階段
get 函數(shù)主要做了四件事情:
1、對(duì)特殊的 key 做了代理 2、通過 Reflect.get 方法求值 3、執(zhí)行 track 函數(shù)收集依賴(最核心) 4、對(duì)計(jì)算的值 res 進(jìn)行判斷,如果它也是數(shù)組或?qū)ο?,則遞歸執(zhí)行 reactive 把 res 變成響應(yīng)式對(duì)象。
Object.defineProperty 是在初始化階段,即定義劫持對(duì)象的時(shí)候就已經(jīng)遞歸執(zhí)行了,而 Proxy 是在對(duì)象屬性被訪問的時(shí)候才遞歸執(zhí)行下一步 reactive,這其實(shí)是一種延時(shí)定義子對(duì)象響應(yīng)式的實(shí)現(xiàn),在性能上會(huì)有較大的提升
收集的依賴就是數(shù)據(jù)變化后執(zhí)行的副作用函數(shù)。
每次 track ,就是把當(dāng)前激活的副作用函數(shù) activeEffect 作為依賴,然后收集到 target 相關(guān)的 depsMap 對(duì)應(yīng) key 下的依賴集合 dep 中。
派發(fā)通知:set 函數(shù)
派發(fā)通知發(fā)生在數(shù)據(jù)更新的階段
set 函數(shù)主要就做兩件事情:
1、通過 Reflect.set 求值 2、通過 trigger 函數(shù)派發(fā)通知(最核心),并依據(jù) key 是否存在于 target 上來確定通知類型,即新增還是修改。
trigger 函數(shù)主要做了四件事情:
1、通過 targetMap 拿到 target 對(duì)應(yīng)的依賴集合 depsMap; 2、創(chuàng)建運(yùn)行的 effects 集合; 3、根據(jù) key 從 depsMap 中找到對(duì)應(yīng)的 effect 添加到 effects 集合; 4、遍歷 effects 執(zhí)行相關(guān)的副作用函數(shù)。
每次 trigger 函數(shù)就是根據(jù) target 和 key ,從 targetMap 中找到相關(guān)的所有副作用函數(shù)遍歷執(zhí)行一遍。
依賴收集和派發(fā)通知的過程中都提到副作用函數(shù),依賴收集過程中我們把 activeEffect(當(dāng)前激活副作用函數(shù))作為依賴收集。
總結(jié)
其實(shí) Vue.js 3.0 在響應(yīng)式的實(shí)現(xiàn)思路和 Vue.js 2.x 差別并不大,主要就是 劫持?jǐn)?shù)據(jù)的方式改成用 Proxy 實(shí)現(xiàn) , 以及收集的依賴由 watcher 實(shí)例變成了組件副作用渲染函數(shù) 。
源碼參考
由于源碼太多,本文就不展示,可直接去: 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
到此這篇關(guān)于vue3 響應(yīng)式的實(shí)現(xiàn)原理的文章就介紹到這了,更多相關(guān)vue3 響應(yīng)式原理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用idea創(chuàng)建第一個(gè)Vue項(xiàng)目
最近在學(xué)習(xí)vue,本文主要介紹了使用idea創(chuàng)建第一個(gè)Vue項(xiàng)目,文中根據(jù)圖文介紹的十分詳盡,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03
Element UI框架中巧用樹選擇器的實(shí)現(xiàn)
這篇文章主要介紹了Element UI框架中巧用樹選擇器的實(shí)現(xiàn),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-12-12

