浅谈Vuejs中nextTick()异步更新队列源码解析
vue官网关于此解释说明如下:
vue2.0里面的深入响应式原理的异步更新队列
官网说明如下:
只要观察到数据变化,Vue将开启一个队列,并缓冲在同一事件循环中发生的所有数据改变。如果同一个watcher被多次触发,只会一次推入到队列中。这种在缓冲时去除重复数据对于避免不必要的计算和DOM操作上非常重要。然后,在下一个的事件循环“tick”中,Vue刷新队列并执行实际(已去重的)工作。Vue在内部尝试对异步队列使用原生的Promise.then和MutationObserver,如果执行环境不支持,会采用setTimeout(fn,0)代替。
例如,当你设置vm.someData=‘newvalue',该组件不会立即重新渲染。当刷新队列时,组件会在事件循环队列清空时的下一个“tick”更新。多数情况我们不需要关心这个过程,但是如果你想在DOM状态更新后做点什么,这就可能会有些棘手。虽然Vue.js通常鼓励开发人员沿着“数据驱动”的方式思考,避免直接接触DOM,但是有时我们确实要这么做。为了在数据变化之后等待Vue完成更新DOM,可以在数据变化之后立即使用Vue.nextTick(callback)。这样回调函数在DOM更新完成后就会调用。例如
源码解析
方法原型以及解析注释如下:
varnextTick=(function(){ varcallbacks=[];//存储需要触发的回调函数 varpending=false;//是否正在等待的标识(false:允许触发在下次事件循环触发callbacks中的回调,true:已经触发过,需要等到下次事件循环) vartimerFunc;//设置在下次事件循环触发callbacks的触发函数 //处理callbacks的函数 functionnextTickHandler(){ pending=false;//可以触发timeFunc varcopies=callbacks.slice(0);//复制callback callbacks.length=0;//清空callback for(vari=0;i我在注释中解释了nextTick()函数的逻辑
上面处理回调的三个方式的使用优先级的原因:因为Promise和MutationObserver和触发的事件在同一个事件循环里面(只不过是运行在微观队列里面),但是setTimeout的回调函数是运行在下次时间循环里面。
优先使用Promise的原因是MutationObserver在ios9.3.3以上版本的UIWebview中运行一段时间后就停止了。
上面代码的注释已经完全说明了代码逻辑。简单理解:将callback推到队列里面,如果还没有执行过在下次事件循环执行触发callback函数。注意:如果使用nextTick()不设置回调函数,而是使用Promise的方式设置回调函数,里面this并不是指向当前的Vue实例,而是指向window(严格模式是undefined);
但是通过上面的分析可知:执行上下文是通过Promise.then()里的回调函数的第一个参数传递的。nextTick()被使用的地方
1、他是全局Vue的一个函数,因此我们可以通过vue直接调用。
2、Vue系统中,用于处理dom更新的操作
Vue中有一个watcher,用于观察数据的变化,然后更新dom。前面我们就知道Vue里面不是每一次数据改变都会触发更新dom,而是将这些操作都缓存在一个队列,在一个事件循环结束之后,刷新队列,统一执行dom更新操作。
functionqueueWatcher(watcher){ varid=watcher.id; if(has[id]==null){ has[id]=true; if(!flushing){ queue.push(watcher); }else{ //ifalreadyflushing,splicethewatcherbasedonitsid //ifalreadypastitsid,itwillberunnextimmediately. vari=queue.length-1; while(i>=0&&queue[i].id>watcher.id){ i--; } queue.splice(Math.max(i,index)+1,0,watcher); } //queuetheflush if(!waiting){ waiting=true; nextTick(flushSchedulerQueue); } } }简单说明上面代码的逻辑,因为是watcher那里的代码,以后会分析到。这里nextTick()的作用,是在此次事件循环结尾的时候刷新watcher检查的dom更新操作。
3、局部Vue触发$nextTick(),在dom更新后执行相应逻辑。
Vue.prototype.$nextTick=function(fn){ returnnextTick(fn,this)//设置nextTick回调函数的上下文环境是当前Vue实例 };上面是renderMinxin中的一段代码,也就是render模块初始化的代码。
总结
如果不了解它的代码,我们会产生理解误区。
1、nextTick()并不会重绘当前页面,并且它也不是在页面重绘才执行,而是在此次事件循环结束后一定会执行的。
2、此方法的触发并不是在页面更新完成才执行,第一条已经说了,但是为什么能在此方法中取到更新后的数据,那是因为dom元素的属性已经在watcher执行flush队列的时候改变了,因此是可以在此时获取的。
证明上述观点的实例:
h5有一个方法requestFrameAnimation(callback),此方法的回调是在页面重绘之前调用。通过实验,更新dom,nextTick()在此方法之前执行。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。