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)
}

 

 
 
posted @ 2020-03-18 18:26  leahtao  阅读(517)  评论(0编辑  收藏  举报