1. 簡述vue響應(yīng)式原理
vue的3個核心類:
- Observer: 給對象的屬性添加getter/setter屬性泄鹏,用于依賴收集和派發(fā)更新
- Dep:用于收集當(dāng)前響應(yīng)式對象的依賴關(guān)系,每一個響應(yīng)式對象都有一個dep實例, dep.subs是一個watcher數(shù)組,當(dāng)數(shù)據(jù)變更時痰哨,會有dep.notify()通知各個watcher
- Watcher:觀察者對象鲁猩,有render watcher, computed watcher 和 user watcher。
* 什么時候觸發(fā)依賴收集:
1. initState , 對computed 屬性初始化時會觸發(fā)computed watcher 依賴收集
2. initState , 對監(jiān)聽屬性初始化時會觸發(fā)user watcher 依賴收集
3. render, render過程觸發(fā)render watcher
* 什么時候觸發(fā)派發(fā)更新
Obect.defineProperty
1. 組件中對響應(yīng)式數(shù)據(jù)進(jìn)行修改會觸發(fā)setter邏輯
2. dep.notify()
3. 遍歷所以subs,調(diào)用每一個watcher實例的update方法
原理總結(jié): 當(dāng)創(chuàng)建vue實例時,vue會遍歷data的屬性纵诞,observer給每一個屬性添加getter/setter對數(shù)據(jù)讀寫進(jìn)行劫持忙干,在getter中收集依賴器予,在setter中派發(fā)更新。所謂在getter中收集依賴捐迫,就是給當(dāng)前屬性添加dep, dep.subs里面收集依賴該屬性的組件乾翔,即一組watcher實例,每一個組件都有相應(yīng)的watcher實例弓乙;所謂在setter中派發(fā)更新末融,就是執(zhí)行當(dāng)前屬性的dep.notify方法,通過遍歷deps.subs暇韧,去執(zhí)行每一個watcher實例的update方法勾习,從而實現(xiàn)視圖的更新。
2. 計算屬性的原理
computed watcher, 計算屬性的監(jiān)聽器
computed watcher, 持有一個dep實例懈玻,通過dirty屬性標(biāo)記計算屬性是否需要重新求值
computed watcher依賴的值發(fā)生改變后就會通知訂閱的watcher進(jìn)行更新巧婶,對于computed watcher會將dirty設(shè)置為true,并進(jìn)行計算屬性方法的調(diào)用, 如:`computed: {test(){}}`
*computed所謂的緩存是什么崔梗?
計算屬性基于它的**響應(yīng)式**依賴澜汤,只有依賴發(fā)生改變時才會重新求值,當(dāng)依賴沒有任何改變時放妈,會拿以前的值而不會重新執(zhí)行計算方法湾盒,即所謂的緩存湿右。注意,只有依賴的屬性是響應(yīng)式的改變時才會觸發(fā)重新求值
* computed緩存的意義是什么罚勾?
當(dāng)計算方法內(nèi)部操作的耗時非常長的時候毅人,比如遍歷一個很大的數(shù)組
3. Vue.nextTick 原理
- 用法:
Vue.nextTick(() => {
})
// 或
await Vue.nextTick()
// TODO
理解:
nextTick 是下一跳的意思吭狡,vue異步執(zhí)行DOM更新的,一旦觀察到數(shù)據(jù)變化丈莺,vue會啟動一個異步隊列划煮,而不是立即執(zhí)行,它會把同一個event loop中發(fā)生數(shù)據(jù)變化的watcher推進(jìn)隊列缔俄,這樣的好處是對一定時間內(nèi)發(fā)生多次變化的視圖可以去重弛秋。
在下一步事件循環(huán)時,Vue會清空隊列俐载,進(jìn)行DOM更新
開啟異步隊列的方式優(yōu)先級: promise.then>MutationObserve->setImmidiate>setTimeout(前面2個是微任務(wù)蟹略,后面2個是宏任務(wù),看瀏覽器支持哪一種)
例如 this.xxx = 1122 并不會馬上更新DOM,而是在Vue清空異步隊列后才會更新
執(zhí)行順序:
如果使用微任務(wù)開啟異步隊列以及DOM的更新:宏任務(wù)-> 微任務(wù)隊列-> UI render (在微任務(wù)隊列里拿新的DOM瞎疼,這是OK的科乎,因為UI render之前其實已經(jīng)是新的DOM了,只是沒有顯示在瀏覽器界面上)
如果使用宏任務(wù)開啟異步隊列: 宏任務(wù)-> UI render -> 宏任務(wù)(在第二個宏任務(wù)里去獲取新的DOM贼急,因為已經(jīng)渲染好了茅茂,所以可以拿到新的DOM)什么時候使用Vue.nextTick
當(dāng)數(shù)據(jù)變化要進(jìn)行一個操作,而這個操作依賴因數(shù)據(jù)變化而變化的DOM太抓,這個操作就放在Vue.nextTick的回調(diào)里空闲,說白了就是要拿到更新后的DOM才可以進(jìn)行這個操作,也可以看出執(zhí)行vue.nextTick后我們可以拿到更新后的DOM
<template>
<div v-if="isShow" ref="test"></div>
</template>
...
async showDiv() {
this.isShow = true
console.log(this.$resf['test']) // undefined
await Vue.nextick()
console.log(this.$resf['test']) // 這里才拿到
this.$resf['test'].xx() // 執(zhí)行依賴這個DOM的操作
}