当前位置: 首页 > Web前端 > JavaScript

NextTick原理分析

时间:2023-03-27 11:41:56 JavaScript

什么是nextTick$nextTick:根据官方文档的解释,它可以在DOM更新后执行一个回调函数,返回一个Promise(如果支持的话)//修改数据vm.msg="Hello";//DOM未更新Vue.nextTick(function(){//DOM已更新});这段对EventLoop的理解应该一眼就明白了。实际上它是在下一次事件循环开始时开始更新DOM,避免中间频繁中断操作导致页面重绘回流。这篇参考官方文档:可能你没有注意到Vue在更新DOM的时候是异步执行的。只要检测到数据变化,Vue就会打开一个队列,缓冲所有发生在同一个事件循环中的数据变化。如果同一个观察者被多次触发,它只会被推入队列一次。这种缓冲时的重复数据删除对于避免不必要的计算和DOM操作非常重要。然后,在下一个事件循环“tick”中,Vue刷新队列并执行实际(去重)工作。Vue内部尝试使用原生的Promise.then、MutationObserver和setImmediate来实现异步队列。如果执行环境不支持,它将使用setTimeout(fn,0)代替。例如设置vm.text='newvalue'时,组件不会立即重新渲染,当队列刷新时,组件会在下一个事件周期'tick'更新,{{message}}

varvm=newVue({el:'#example',data:{message:'123'}})vm.message='newmessage'//改变数据vm.$el.textContent==='newmessage'//falseVue.nextTick(function(){vm.$el.textContent==='newmessage'//true})通常在设置完this.xx='xx后立即获取'data$nextTick只会在获取到最新的DOM数据时使用,因为DOM的更新是异步的,所以需要这个方法来获取。vue异步更新策略更新流程(源码分析)当数据被修改时,watcher会监听到变化,然后将变化入队:/**订阅者接口。*将在依赖项更改时调用。*/观察者。prototype.update=functionupdate(){/*istanbulignoreelse*/if(this.lazy){this.dirty=true;}elseif(this.sync){this.run();}else{queueWatcher(this);}};并使用nextTick方法添加一个flushScheduleQueue回调/***将观察者推入观察者队列。*具有重复ID的作业将被跳过,除非它在刷新队列时被推送。*/functionqueueWatcher(watcher){varid=watcher.id;if(has[id]==null){has[id]=true;如果(!flushing){queue.push(watcher);}else{//如果已经刷新,根据它的id拼接watcher//如果已经超过它的id,它将立即运行。vari=queue.length-1;while(i>index&&queue[i].id>watcher.id){i--;}queue.splice(i+1,0,观察者);}//队列同花顺if(!waiting){waiting=true;如果(!config.async){flushSchedulerQueue();返回;}nextTick(flushSchedulerQueue);}}}flushScheduleQueue添加到回调数组中,函数nextTick(cb,ctx){var_resolve;callbacks.push(function(){if(cb){try{cb.call(ctx);//!!cb是添加的回调}catch(e){handleError(e,ctx,"nextTick");}}否则如果(_resolve){_resolve(ctx);}});if(!pending){//异步执行见timerFuncpending=true;定时器功能();}//$flow-disable-lineif(!cb&&typeofPromise!=="undefined"){returnnewPromise(function(resolve){_resolve=resolve;});}}timerFunc操作异步执行,顺序判断使用:Promise.then=>MutationObserver=>setImmediate=>setTimeoutvartimerFunc;if(typeofPromise!=="undefined"&&isNative(Promise)){varp=Promise.resolve();timerFunc=function(){p.then(flushCallbacks);//1.Promise.thenif(isIOS){setTimeout(noop);}};isUsingMicroTask=true;}elseif(!isIE&&typeofMutationObserver!=="undefined"&&(isNative(MutationObserver)||MutationObserver.toString()==="[objectMutationObserverConstructor]")){//2.MutationObserver变量计数器=1;varobserver=newMutationObserver(flushCallbacks);vartextNode=document.createTextNode(字符串(计数器));observer.observe(textNode,{characterData:true,});timerFunc=function(){counter=(counter+1)%2;textNode.data=String(计数器);};isUsingMicroTask=true;}elseif(typeofsetImmediate!=="undefined"&&isNative(setImmediate)){//3.setImmediatetimerFunc=function(){setImmediate(flushCallbacks);}};}else{//4.setTimeouttimerFunc=function(){setTimeout(flushCallbacks,0);};}flushCallbacks遍历所有的回调并执行functionflushCallbacks(){pending=false;var副本=callbacks.slice(0);回调.length=0;对于(vari=0;i