当前位置: 首页 > 科技观察

《源码分析》NextTick的作用是什么

时间:2023-03-13 01:47:31 科技观察

Vue中每次监听数据变化,都会调用notify通知依赖更新,并触发watcher中的update方法。update(){/*istanbulignoreelse*/if(this.lazy){}elseif(this.sync){}else{this.get()//queueWatcher(this)}}如果watcher中的get方法用于重新渲染Components,如果在渲染过程中多次更新数据,会多次触发同一个watcher,从而导致重复的数据计算和DOM操作。如下图,DOM在修改消息3次后被操作了3次。为了解决上述问题,不直接调用get方法,而是将每次调用update方法后需要批量处理的wather暂存在一个队列中。如果同一个watcher被触发多次,通过watcher的id属性进行去重,只会被推入队列一次。然后,在等待所有同步代码执行后,在下一个事件循环中,Vue刷新队列并执行实际的(去重)工作。lethas:{[key:number]:?true}={}letwaiting=falseexportfunctionqueueWatcher(watcher:Watcher){constid=watcher.id//去重watcherif(has[id]==null){has[id]=truequeue.push(watcher);if(!waiting){//throttlingwaiting=truenextTick(flushSchedulerQueue)}}调用watcher的run方法异步更新DOMlethas:{[key:number]:?true}={}functionflushSchedulerQueue(){letwatcher,idqueue.sort((a,b)=>a.id-b.id)for(index=0;index{if(cb){try{cb.call(ctx)}catch(e){handleError(e,ctx,'nextTick')}}})if(!pending){pending=truetimerFunc()}}nextTick将所有的回调函数暂时存放在一个队列中,然后通过异步调用更新函数flushCallbacks(){pending=falseconstcopies=callbacks.slice(0)callbacks.length=0for(leti=0;i{p.then(flushCallbacks)}}MutationObserverMutationObserver会在指定DOM改变时被调用。创建一个文本DOM,通过监听字符值的变化,在文本字符发生变化时调用回调函数。if(!isIE&&typeofMutationObserver!=='undefined'&&(isNative(MutationObserver)||MutationObserver.toString()==='[objectMutationObserverConstructor]')){letcounter=1constobserver=newMutationObserver(flushCallbacks)consttextNode=document.createTextNode(String(counter))observer.observe(textNode,{characterData:true})timerFunc=()=>{counter=(counter+1)%2textNode.data=String(counter)}}setImmediatesetImmediate这个方法用来设置一些需要runcontinuely将操作放在另一个函数中,这个替换函数在浏览器完成后面的其他语句后立即执行。if(typeofsetImmediate!=='undefined'&&isNative(setImmediate)){timerFunc=()=>{setImmediate(flushCallbacks)}}else{timerFunc=()=>{setTimeout(flushCallbacks,0)}}总结vue渲染DOM时间在set方法中触发去依赖更新。在更新过程中,watcher并不是每次都去执行触发DOM更新,而是通过wather去重后异步调用nextTick来触发DOM更新。nextTick()是一个异步函数。在异步函数中,nextTick传入的回调函数cb是通过队列进行批处理的,但是队列不是同时执行的,通过throttling顺序执行。