Vue源码之异步批量任务更新
假设页面有四个地方需要更新属性,那我们希望不要更新四次,而是一次性更新。
防止不停的更新
把需要更新的watcher先存起来 放进一个异步队列
把重复的watcher过滤掉 等待这轮更新完就清空队列 就是说等待主执行栈执行完了就执行异步任务,也可以理解为页面所有属性都赋值完再执行这个异步方法
/**
* 重要部分updata()方法
* 加入页面有四个地方需要更新属性,那我们希望不要更新四次,而是一次性更新
* 防止不停的更新
把需要更新的watcher先存起来 放进一个异步队列queueWatcher,然后通过nextTick异步执行
把重复的watcher过滤掉
等待这轮更新完就清空队列 就是说等待主执行栈执行完了就执行异步任务,也可以理解为页面所有属性都赋值完再执行这个异步方法
*/
updata(){ //如果立即调用get 会导致页面刷新频繁 用异步更新来解决
// this.get()
queueWatcher(this) //queueWatcher异步队列
}
run(){
this.get()
}
}
//渲染watcher、计算属性、vm.$watch 都属于Watcher实例
export default Watcher
function flushQueue () {
queue.forEach(watcher => watcher.run) //执行更新
//清空,下次使用
has = {}
queue = []
}
let has ={};
let queue = []
function queueWatcher(watcher){
let id = watcher.id
if(has[id] == null){
has[id] =true
queue.push(watcher) //相同的Watcher只会存一个到queue中
//延迟清空队列
// setTimeout(flushQueue,0) 这个方法也可以但vue内部原理是nextTick
nextTick(flushQueue)
}
}
let callbacks =[] //有可能用户也会写一个nextTick方法,这时候就需要把nextTick的回调函数放进一个数组里面,再依次执行,
function nextTick(cb){
callbacks.push(cb)
//异步刷新这个callbacks
//异步任务先执行微任务在执行宏任务,微任务:Promise, mutationObserver, 宏任务:setImmediate setTimeout
let timeFunc = () => {
flushCallbacks()
}
//判断当前浏览器执行的异步方法
if(Promise) {
return Promise.resolve().then(timeFunc)
}
if(MutationObserver){ //创建并返回一个新的 MutationObserver 它会在指定的DOM发生变化时被调用
let observe = new MutationObserver(timeFunc);
let textNode = document.createTextNode(1)
observe.observe(textNode,{characterData:true})
textNode.textContent(2)
return
}
if(setImmediate) {
return setImmediate(timeFunc)
}
//以上方法都不支持的时候就调用setTimeout setTimeout是宏任务,会在下一轮执行 事件循环机制的相关知识,但我们希望尽量再本轮执行,所以先判断支不支持微任务
setTimeout(timeFunc,0)
}
不积跬步无以至千里