相信大家在写vue项目的时候,都会发现一个神奇的api,Vue.nextTick。为什么说神奇,那是因为当你做一些没有生效的操作的时候,把这个操作写在Vue.nextTick里面,它就会神奇的生效。那么是什么原因呢?让我们一起解决吧。简单的说,vue对响应式的实现并不是在数据发生变化后立即改变DOM,而是按照一定的策略异步执行DOM更新。数据修改后,视图不会立即更新,但是同一个事件循环机制中的所有数据必须是相等的,在修改完成后,DOM更新nextTick会让我们在下一个DOM更新周期后执行一个延迟回调获取更新的DOM。事件循环机制在讨论Vue.nextTick之前,需要弄清楚事件循环机制,它是实现的基石,下面我们来看一下。在浏览器环境中,我们可以将我们的执行任务分为宏任务和微任务。Macrotasks:包括整体代码脚本、setTimeout、setInterval、setImmediate、I/O操作、UI渲染microtasks:Promise.then、MuationObserver事件循环的顺序决定了js代码的执行顺序。事件循环如下:代码中解释,浏览器中事件循环的顺序与下面代码相同:for(macroTaskofmacroTaskQueue){//1.执行一个宏任务handleMacroTask();//2.执行所有微任务for(microTaskofmicroTaskQueue){handleMicroTask(microTask);}}Vue数据驱动视图处理(异步改变DOM)exportdefault{data(){return{number:0};},方法:{handleClick(){for(leti=0;i<10000;i++){this.count++;}}}}分析上面的代码:点击按钮时,count会循环变化10000次。然后每次count+1,都会触发count的setter方法,然后修改真实的DOM。按照这个逻辑,整个过程中DOM会更新10000次。我们都知道DOM操作是非常昂贵的,这样的操作是完全没有必要的。因此,Vue在派发更新的时候内部进行了优化,即不会在每次数据变化时触发watcher回调,而是先将这些watcher加入到一个队列queueWatcher中,然后在nextTick之后计数增加10000时执行flushSchedulerQueue处理,第二次,Vue会先把对应的Watcher对象压入一个queue队列,等待下一个tick执行。下一次tick修改界面不需要执行10000个相同的Watcher对象,只需要执行一个Watcher对象就可以将界面上的0变为10000。Vue.nextTick的原理在上一节中已经知道。,Vue中的数据变化=>DOM变化是一个异步过程,一旦观察到数据变化,Vue会开启一个任务队列,然后在同一个事件循环(Eventloop)Watcher中观察数据变化(在Vue源码中Wacher类用于将Dep类收集的依赖项更新到此队列中。如果这个观察者被多次触发,它只会被推送到队列中一次。这种缓冲行为可以有效去除重复数据导致的不必要的计算和DOM操作。在下一个事件循环中,Vue将清除队列并执行必要的DOM更新。nextTick的作用是在数据发生变化后,等待Vue完成对DOM的更新。您可以在数据更改后立即使用Vue.nextTick(callback)。JS是单线程的,有事件循环机制。nextTick的实现是使用了事件循环Tasks和microtasks的宏。vue中next-tick.js的源码如下isUsingMicroTask=false//首先定义一个callbacks数组存放nextTick,在下一??个tick处理这些回调函数之前,//所有的cb都会存放在这个callbacks数组中constcallbacks=[]//pending是一个flag,代表等待状态letpending=false//最后执行flushCallbacks()方法,遍历callbacks数组,执行functionflushCallbacks()里面的各个函数{pending=falseconstcopies=callbacks.slice(0)callbacks.length=0for(leti=0;i{p.then(flushCallbacks)if(isIOS)setTimeout(noop)}isUsingMicroTask=true}elseif(!isIE&&typeofMutationObserver!=='你ndefined'&&(isNative(MutationObserver)||//PhantomJS和iOS7.xMutationObserver.toString()==='[objectMutationObserverConstructor]')){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)){timerFunc=()=>{setImmediate(flushCallbacks)}}else{timerFunc=()=>{setTimeout(flushCallbacks,0)}}导出函数nextTick(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()}//$flow-disable-lineif(!cb&&typeofPromise!=='undefined'){returnnewPromise(resolve=>{_resolve=resolve})}}参考vue实战视频解释:学习一下,目前浏览器平台没有实现nextTick方法,所以Vue.js源码使用Promise、setTimeout、setImmediate等在microtask(或task)中创建事件,目的是在之后执行当前调用栈(不是必须立即)执行这个事件nextTick的调用方法回调函数方法:Vue.nextTick(callback)Promise方法:Vue.nextTick().then(callback)实例方法:vm.$nextTick(callback)Vue.nextTick在applicationcreated生命周期中,当DOMcreatedhook函数执行时,DOM并没有真正挂载和渲染。这个时候是不能操作DOM的。我们将DOM操作代码放入nextTick中,等待下一轮事件循环开始。DOM已经挂载完毕,这个操作对应的是mounted钩子函数,因为mounted执行的时候已经完成了所有的DOM挂载。created(){vm.$nextTick(()=>{//不要使用this.$nextTick()方法操作DOM,会报错this.$refs.test.innerHTML="DOM被操作increated"});}修改Data,获取DOM值当我们修改data中的数据时,我们无法通过操作DOM立即获取到里面的值{{msg}}
v-show/v-if由隐藏变为显示点击按钮显示原本用v-show=false或v-if隐藏的输入框,并获得焦点或获得它宽和高场景