vue源碼解析computed多次訪問會(huì)有死循環(huán)原理
正文
在上一篇中,我們仿vue源碼自己實(shí)現(xiàn)了一個(gè)computed。
寫了如下的測試代碼。
const { reactive, effect, ref, computed } = Vue const obj = reactive({ name: '張三' }) // C1 const computedObj = computed(() => { console.log('computed') return '姓名:' + obj.name }) // e1 effect(() => { document.querySelector('#app').innerHTML = computedObj.value document.querySelector('#app').innerHTML = computedObj.value }) setTimeout(() => { obj.name = '李四' }, 2000)
我們發(fā)現(xiàn)computed多次訪問的時(shí)候,會(huì)出現(xiàn)死循環(huán)。
為什么會(huì)有死循環(huán)
這個(gè)問題出在哪了呢?
我們先看一下這段代碼的執(zhí)行情況。(當(dāng)時(shí)看源碼,我也是理了好幾遍,才理出頭緒。有條件的話,還是建議clone代碼自己調(diào)試一遍)
我們直接看effect。
1.先執(zhí)行effect函數(shù),設(shè)置activeEffect為傳進(jìn)來的參數(shù),也就是掛載dom的這個(gè)函數(shù),我們把這個(gè)函數(shù)就叫e1。
//叫e1 () => { document.querySelector('#app').innerHTML = computedObj.value document.querySelector('#app').innerHTML = computedObj.value }
2.執(zhí)行e1函數(shù)
第一次執(zhí)行computedObj.value,此時(shí)會(huì)執(zhí)行如下代碼。
先是收集依賴,將e1收集起來;
【此時(shí),key為computedObj, activeEffect為e1】
因?yàn)榇藭r(shí),_dirty是true,所以會(huì)執(zhí)行computed計(jì)算函數(shù)。
執(zhí)行effect.run,也就是下面這段邏輯,我們把這個(gè)函數(shù)叫做c1
// 叫c1 () => { console.log('con') return '姓名:' + obj.name }
執(zhí)行obj.name 觸發(fā)reactive的get,將C1收集起來。
【此時(shí),key為obj, activeEffect被改為C1】
3 第二次執(zhí)行computedObj.value
依舊會(huì)觸發(fā)一次依賴收集。
【此時(shí),key為computedObj, activeEffect已經(jīng)被改為為C1了】
此時(shí)_dirty為false,則不會(huì)執(zhí)行計(jì)算。
effct執(zhí)行完畢,此時(shí)computedObj有兩個(gè)依賴,分別是e1和c1。obj上有一個(gè)依賴,為c1
4.在2秒后,觸發(fā)obj.name的set事件,則觸發(fā)obj上的依賴函數(shù),開始遍歷執(zhí)行。
注意,因?yàn)閏omputed中使用了scheduler,所以此時(shí)的c1。
所以會(huì)執(zhí)行如下代碼
this.effect = new ReactiveEffect(getter, () => { // 判斷當(dāng)前臟的狀態(tài),如果為 false,表示需要《觸發(fā)依賴》 if (!this._dirty) { // 將臟置為 true,表示 this._dirty = true triggerRefValue(this) } })
5.執(zhí)行triggerRefValue(this)
triggerRefValue(this)會(huì)觸發(fā)computedObj上的依賴。
此時(shí)dirty = true, 并遍歷執(zhí)行e1,c1依賴函數(shù)。
for (const effect of effects) { triggerEffect(effect) }
6.當(dāng)執(zhí)行e1函數(shù)時(shí),又會(huì)觸發(fā)computed.get,并將e1加入依賴。
此時(shí)【key為computedObj, activeEffect為e1】
由于此時(shí),_dirty是true,則又會(huì)執(zhí)行run,重新計(jì)算。
此時(shí)【key為, activeEffect為c1】
7.當(dāng)執(zhí)行c1時(shí),因?yàn)橹癳1函數(shù)已經(jīng)將_dirty改為false了,于是又會(huì)開始執(zhí)行triggerRefValue(this),遍歷computedObj上的依賴c1和e1。
因?yàn)榇藭r(shí)computedObj依舊是有e1和c1兩個(gè)依賴,又會(huì)重新回到第5步,造成死循環(huán)。
如何解決死循環(huán)
那找到了問題,如何解決呢?
其實(shí)方法也很簡單,在第6步的時(shí)候,我們只要確保讓c1先執(zhí)行,e1后執(zhí)行就行,
先執(zhí)行c1時(shí),因?yàn)榇藭r(shí)dirty是true,所以不會(huì)重復(fù)執(zhí)行triggerRefValue(this)。
然后再執(zhí)行e1,添加依賴。
vue源碼也是這么實(shí)現(xiàn)的,先把computed的依賴執(zhí)行完,然后再執(zhí)行其他依賴。
這也就是為什么,vue源碼中觸發(fā)依賴,有兩次遍歷。
export function triggerEffects( dep: Dep | ReactiveEffect[], debuggerEventExtraInfo?: DebuggerEventExtraInfo ) { // spread into array for stabilization const effects = isArray(dep) ? dep : [...dep] for (const effect of effects) { if (effect.computed) { triggerEffect(effect, debuggerEventExtraInfo) } } for (const effect of effects) { if (!effect.computed) { triggerEffect(effect, debuggerEventExtraInfo) } } }
總結(jié)
不管是數(shù)據(jù)的響應(yīng)式,還是computed的實(shí)現(xiàn),亦或是vue組件的更新。
本質(zhì)都是通過effec偵聽器來實(shí)現(xiàn)的。
對于effec有幾點(diǎn)要理解清楚。
1.activeEffect是會(huì)不斷變化的,這就導(dǎo)致,同樣的代碼,可能收集到的依賴函數(shù)是不一樣的。
比如上文的computedObj對象,兩次收集得依賴就是不同的,因?yàn)榈诙螞]有計(jì)算函數(shù)的執(zhí)行。
2.設(shè)置了scheduler,再次觸發(fā)的時(shí)候,執(zhí)行函數(shù)就變成了scheduler。
比如上文收集的依賴函數(shù)c1。
到這里,effet應(yīng)該有比較深入的認(rèn)識了,下一篇再講講如何通過effec偵聽器來控制更新的時(shí)機(jī),來實(shí)現(xiàn)組件更新。
以上就是vue源碼解析computed多次訪問會(huì)有死循環(huán)原理的詳細(xì)內(nèi)容,更多關(guān)于vue computed多次訪問死循環(huán)的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
echarts設(shè)置tootip輪播切換展示(vue3搭配vue-echarts粘貼即用)
這篇文章主要為大家介紹了echarts設(shè)置tootip輪播切換展示效果,vue3搭配vue-echarts粘貼即用,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2023-10-10分分鐘學(xué)會(huì)vue中vuex的應(yīng)用(入門教程)
本篇文章主要介紹了vue中vuex的應(yīng)用(入門教程),詳細(xì)的介紹了vuex.js和應(yīng)用方法,有興趣的可以了解一下2017-09-09Ant?Design?Vue?修改表格頭部樣式的詳細(xì)代碼
這篇文章主要介紹了Ant?Design?Vue?修改表格頭部樣式,首先用到的是customHeaderRow這個(gè)API,類型是一個(gè)函數(shù),本文通過完整代碼給大家詳細(xì)講解,需要的朋友可以參考下2022-10-10Vue實(shí)現(xiàn)渲染數(shù)據(jù)后控制滾動(dòng)條位置(推薦)
這篇文章主要介紹了Vue實(shí)現(xiàn)渲染數(shù)據(jù)后控制滾動(dòng)條位置,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-12-12vue 實(shí)現(xiàn)基礎(chǔ)組件的自動(dòng)化全局注冊
這篇文章主要介紹了vue 實(shí)現(xiàn)基礎(chǔ)組件的自動(dòng)化全局注冊的方法,幫助大家更好的理解和使用vue框架,感興趣的朋友可以了解下2020-12-12vue實(shí)現(xiàn)自定義公共組件及提取公共的方法
這篇文章主要介紹了vue實(shí)現(xiàn)自定義公共組件及提取公共的方法,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-05-05