vue源碼解析computed多次訪問會有死循環(huán)原理
正文
在上一篇中,我們仿vue源碼自己實現(xiàn)了一個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多次訪問的時候,會出現(xiàn)死循環(huán)。
為什么會有死循環(huán)
這個問題出在哪了呢?
我們先看一下這段代碼的執(zhí)行情況。(當時看源碼,我也是理了好幾遍,才理出頭緒。有條件的話,還是建議clone代碼自己調試一遍)
我們直接看effect。
1.先執(zhí)行effect函數(shù),設置activeEffect為傳進來的參數(shù),也就是掛載dom的這個函數(shù),我們把這個函數(shù)就叫e1。
//叫e1 () => { document.querySelector('#app').innerHTML = computedObj.value document.querySelector('#app').innerHTML = computedObj.value }
2.執(zhí)行e1函數(shù)
第一次執(zhí)行computedObj.value,此時會執(zhí)行如下代碼。
先是收集依賴,將e1收集起來;
【此時,key為computedObj, activeEffect為e1】
因為此時,_dirty是true,所以會執(zhí)行computed計算函數(shù)。
執(zhí)行effect.run,也就是下面這段邏輯,我們把這個函數(shù)叫做c1
// 叫c1 () => { console.log('con') return '姓名:' + obj.name }
執(zhí)行obj.name 觸發(fā)reactive的get,將C1收集起來。
【此時,key為obj, activeEffect被改為C1】
3 第二次執(zhí)行computedObj.value
依舊會觸發(fā)一次依賴收集。
【此時,key為computedObj, activeEffect已經被改為為C1了】
此時_dirty為false,則不會執(zhí)行計算。
effct執(zhí)行完畢,此時computedObj有兩個依賴,分別是e1和c1。obj上有一個依賴,為c1
4.在2秒后,觸發(fā)obj.name的set事件,則觸發(fā)obj上的依賴函數(shù),開始遍歷執(zhí)行。
注意,因為computed中使用了scheduler,所以此時的c1。
所以會執(zhí)行如下代碼
this.effect = new ReactiveEffect(getter, () => { // 判斷當前臟的狀態(tài),如果為 false,表示需要《觸發(fā)依賴》 if (!this._dirty) { // 將臟置為 true,表示 this._dirty = true triggerRefValue(this) } })
5.執(zhí)行triggerRefValue(this)
triggerRefValue(this)會觸發(fā)computedObj上的依賴。
此時dirty = true, 并遍歷執(zhí)行e1,c1依賴函數(shù)。
for (const effect of effects) { triggerEffect(effect) }
6.當執(zhí)行e1函數(shù)時,又會觸發(fā)computed.get,并將e1加入依賴。
此時【key為computedObj, activeEffect為e1】
由于此時,_dirty是true,則又會執(zhí)行run,重新計算。
此時【key為, activeEffect為c1】
7.當執(zhí)行c1時,因為之前e1函數(shù)已經將_dirty改為false了,于是又會開始執(zhí)行triggerRefValue(this),遍歷computedObj上的依賴c1和e1。
因為此時computedObj依舊是有e1和c1兩個依賴,又會重新回到第5步,造成死循環(huán)。
如何解決死循環(huán)
那找到了問題,如何解決呢?
其實方法也很簡單,在第6步的時候,我們只要確保讓c1先執(zhí)行,e1后執(zhí)行就行,
先執(zhí)行c1時,因為此時dirty是true,所以不會重復執(zhí)行triggerRefValue(this)。
然后再執(zhí)行e1,添加依賴。
vue源碼也是這么實現(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) } } }
總結
不管是數(shù)據(jù)的響應式,還是computed的實現(xiàn),亦或是vue組件的更新。
本質都是通過effec偵聽器來實現(xiàn)的。
對于effec有幾點要理解清楚。
1.activeEffect是會不斷變化的,這就導致,同樣的代碼,可能收集到的依賴函數(shù)是不一樣的。
比如上文的computedObj對象,兩次收集得依賴就是不同的,因為第二次沒有計算函數(shù)的執(zhí)行。
2.設置了scheduler,再次觸發(fā)的時候,執(zhí)行函數(shù)就變成了scheduler。
比如上文收集的依賴函數(shù)c1。
到這里,effet應該有比較深入的認識了,下一篇再講講如何通過effec偵聽器來控制更新的時機,來實現(xiàn)組件更新。
以上就是vue源碼解析computed多次訪問會有死循環(huán)原理的詳細內容,更多關于vue computed多次訪問死循環(huán)的資料請關注腳本之家其它相關文章!
相關文章
echarts設置tootip輪播切換展示(vue3搭配vue-echarts粘貼即用)
這篇文章主要為大家介紹了echarts設置tootip輪播切換展示效果,vue3搭配vue-echarts粘貼即用,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步2023-10-10Vue實現(xiàn)渲染數(shù)據(jù)后控制滾動條位置(推薦)
這篇文章主要介紹了Vue實現(xiàn)渲染數(shù)據(jù)后控制滾動條位置,本文通過圖文并茂的形式給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下2019-12-12