前言在项目中,经常需要在视图层立即显示数据,而有时由于异步数据传输,页面不会立即显示在页面上。这时候就需要用到Vue提供的nextTick方法了。主要原因是Vue的数据视图是异步更新的。官方的解释是:Vue对响应式的实现,并不是在数据发生变化后立即改变DOM,而是按照一定的策略来更新DOM。其中提到的事件循环也是前端面试中经常被问到的一个点。本文不做详细展开。有兴趣的同学可以参考这篇文章了解EventLoop(彻底解决这类面试题)踩坑目录模板案例数据在视图上显示兄弟组件间的异步数据传递$nextTick源码实现解析和踩坑案例模板案例数据显示在视图上[bug描述]页面点击reset后,模板视图会渲染为固定数据的视图[bug分析]需要点击后立即显示在页面上。这是一个典型的需要应用nextTick的场景。对于那些需要遵守数组监听的方法,这里直接新建一个,也就是说每次地址都会变,所以可以监听asyncresetTemplate(){this.template=[];awaitthis.$nextTick(function(){this.template=[{week:'1',starttime:'00:00:00',endtime:'00:00:00'},{week:'2',开始时间:'00:00:00',结束时间:'00:00:00'},{周:'3',开始时间:'00:00:00',结束时间:'00:00:00'},{周:'4',圣艺术时间:'00:00:00',结束时间:'00:00:00'},{周:'5',开始时间:'00:00:00',结束时间:'00:00:00'},{周:'6',开始时间:'00:00:00',结束时间:'00:00:00'},{周:'7',开始时间:'00:00:00',结束时间:'00:00:00'}];});}兄弟组件之间的异步数据传输【bug描述】页面修改弹窗中的输入框字段需要重写为对应的字段,通过Props传数据后不会直接修改数据【bug分析】]这种场景下,数据通过子组件发射给父组件,父组件获取数据后通过props传给弹窗。v-model中的数据获取是异步的【解决】这个是比较少见的使用$nextTick处理v-model异步数据传输方式(ps:关于emit/on的发布和订阅的介绍,有兴趣的同学可以看这篇文章【vuepublishsubscribermode$emit,$on】(https://blog.csdn.net/qq_4277...,采用的方法是父组件的数据延迟到下一个tick传给子组件,子组件及时渲染到对应的页面上,除了这个方法,还有其他的方法,具体可以参考这篇文章讲解Vue父组件向子组件传递props异步数据的问题编辑(数据){this.isManu=true;让[content,pos]=数据;this.manuPos=pos;this.form=content;this.$nextTick(函数(){this.$refs.deviceEdit.form.deviceid=content.deviceId;this.$refs.deviceEdit.form.devicename=content.deviceName;this.$refs.deviceEdit.form.devicebrand=content.deviceBrand;this.$refs.deviceEdit.form.devicegroup=content.deviceGroup;this.$refs.deviceEdit.form.mediatrans=content.mediaTrans;this.$refs.deviceEdit.form.cloudstorage=content.cloudStorage;this.$refs.deviceEdit.form.longitude=content.longitude;this.$refs.deviceEdit.form.latitude=content.latitude;this.$refs.deviceEdit.form.altitude=content.altitude;})},$nextTick源代码实现2.5之前的版本:/***延迟任务以异步执行它。*/exportconstnextTick=(function(){constcallbacks=[]letpending=falselettimerFuncfunctionnextTickHandler(){pending=falseconstcopies=callbacks.slice(0)callbacks.length=0for(leti=0;i=9.3.3中的UIWebView中存在严重错误。它//在触发几次后完全停止工作...因此,如果本机//Promise可用,我们将使用它:/*istanbulignoreif*/if(typeofPromise!=='undefined'&&isNative(Promise)){varp=Promise.resolve()varlogError=err=>{console.error(err)}timerFunc=()=>{p.then(nextTickHandler).catch(logError)//在有问题的UIWebView中,Promise.then并没有完全中断,但是//它可能会陷入一种奇怪的状态,回调被推入//微任务队列但队列没有被刷新,直到浏览器//需要做一些其他工作,例如处理一个定时器。因此我们可以//“强制”th通过添加一个空计时器来刷新微任务队列。if(isIOS)setTimeout(noop)}}elseif(!isIE&&typeofMutationObserver!=='undefined'&&(isNative(MutationObserver)||//PhantomJS和iOS7.xMutationObserver.toString()==='[objectMutationObserverConstructor]')){//在原生Promise不可用的地方使用MutationObserver,//例如PhantomJS、iOS7、Android4.4varcounter=1varobserver=newMutationObserver(nextTickHandler)vartextNode=document.createTextNode(String(counter))observer.observe(textNode,{characterData:true})timerFunc=()=>{计数器=(counter+1)%2textNode.data=String(counter)}}else{//回退到setTimeout/*istanbulignorenext*/timerFunc=()=>{setTimeout(nextTickHandler,0)}}返回函数queueNextTick(cb?:Function,ctx?:Object){let_resolvecallbacks.push(()=>{if(cb){try{cb.call(ctx)}catch(e){handleError(e,ctx,'nextTick')}}elseif(_resolve){_resolve(ctx)}})if(!pending){pending=truetimerFunc()}if(!cb&&typeofPromise!=='undefined'){returnnewPromise((resolve,reject)=>{_resolve=resolve})}}})()2.5之后的版本/*@flow*//*globalsMutationObserver*/import{noop}from'shared/util'import{handleError}from'./error'import{isIE,isIOS,isNative}from'./env'exportletisUsingMicroTask=falseconstcallbacks=[]letpending=falsefunctionflushCallbacks(){pending=falseconstcopies=callbacks.slice(0)callbacks.length=0for(leti=0;i=9.3.3中的//UIWebView中存在严重错误。它//在触发几次后完全停止工作...所以,如果本机//Promise可用,我们将使用它:/*istanbul接下来忽略$flow-disable-line*/if(typeofPromise!=='undefined'&&isNative(Promise)){constp=Promise.resolve()timerFunc=()=>{p.then(flushCallbacks)//在有问题的UIWebViews中,Promise.then并没有完全中断,但是//它可能会陷入一种奇怪的状态,回调被推入//微任务队列,但队列不会被刷新,直到浏览器//需要做一些其他的工作,例如处理一个定时器。因此我们可以通过添加一个空定时器来“强制”刷新微任务队列。if(isIOS)setTimeout(noop)}isUsingMicroTask=true}elseif(!isIE&&typeofMutationObserver!=='undefined'&&(isNative(MutationObserver)||//PhantomJS和iOS7.xMutationObserver.toString()==='[objectMutationObserverConstructor]')){//在原生Promise不可用的地方使用MutationObserver,//例如PhantomJS,iOS7,Android4.4//(#6466MutationObserverisu在IE11中不可靠)letcounter=1constobserver=newMutationObserver(flushCallbacks)consttextNode=document.createTextNode(String(counter))observer.observe(textNode,{characterData:true})timerFunc=()=>{counter=(counter+1)%2textNode.data=String(counter)}isUsingMicroTask=true}elseif(typeofsetImmediate!=='undefined'&&isNative(setImmediate)){//回退到setImmediate。//从技术上讲,它利用了(宏)任务队列,//但它仍然是比setTimeout更好的选择。timerFunc=()=>{setImmediate(flushCallbacks)}}else{//回退到setTimeout。timerFunc=()=>{setTimeout(flushCallbacks,0)}}导出函数nextTick(cb?:Function,ctx?:Object){let_resolvecallbacks.push(()=>{if(cb){try{cb.调用(ctx)}catch(e){handleError(e,ctx,'nextTick')}}elseif(_resolve){_resolve(ctx)}})if(!pending){pending=truetimerFunc()}//$flow-disable-lineif(!cb&&typeofPromise!=='undefined'){returnnewPromise(resolve=>{_resolve=resolve})}}版本不同主要是timeFunc的异步函数的优先级不同,2.5以后有一些区别,但主要问题是是否暴露微任务函数和宏任务函数(ps:2.5以后的版本以上是2.6.11)2.5之前的版本:Promise=>MutationObserver=>setTimeout2.5之后的版本:setImmediate=>MessageChannel=>Promise=>setTimeout总结js的异步执行机制是前端同学必须掌握的知识,其中其中nextTick就是一个非常典型的代表,在node中也有nextTick相关的方法。面试中经常会问到相关方法的实现。深入了解js的基本方法和特点,对于避免前端开发中的坑还是很有用的。每次出现问题,几乎所有的面试题都会出现。相关知识的展示和打好基础,永远是工程师崛起的坚实基础!letcallbacks=[]letpending=falsefunctionnextTick(cb){callbacks.push(cb)if(!pending){pending=truesetTimeout(flushCallback,0)}}functionflushCallback(){pending=false让副本=回调.slice()callbacks.length=0copies.forEach(copy=>{copy()})}参考Vue.nextTick的原理和使用,简单了解VuenextTick源码分析中的nextTickvuenextTick机制Vue源码解析Nodejs的nextTick的nextTick和setTimeout的解析Nodejs的nextTick和this.$nextTick()在Vue.js中使用vue发布订阅者模式$emit,$onVue父组件传递props问题详解异步数据到子组件了解一次EventLoop(彻底解决这类面试题)