1. 作用
Vue中视图更新是异步的,如果想要在视图更新后操作逻辑,例如获取DOM,就需要使用该方法来操作。
2. 调用顺序?
可以先看看上图,打印的答案是多少?我直接说结果,是10。
可能我们都听说过,nextTick是个异步方法,按道理应该先同步的执行,再轮到它执行,结果应该是300吧。那这里为什么结果是10呢?这个就要从源码去分析了,继续往下看。
来看下Vue2的源码
这里的源码主要表达的意思是,我们调用nextTick的时候,vue里会用一个数组把你的回调函数存储起来先,就是图中的callbacks。接着会去执行timerFunc方法。
timerFunc这个放到后面再详细说名,现在这里先简单认为它是个Promise的方法就行
到这里我们就应该可以明白了,用户自定义在nextTick传进来的回调函数,其实是给callbacks数组存储起来先了。
然后此时我们页面代码就会执行到下面的赋值操作
这个修改响应式变量,就会让vue里面去触发watcher队列方法,可以看下图源码
(watcher主要就是改变响应式数据会去调用自身的update方法触发组件重新渲染,但它不会一收到一次数据改变就去更新,会收集多次数据一次更新)
这个方法主要是说多次改变数据,watcher需要触发更新的时候,会有个watcher队列,会阻止重复的watcher加入。需要队列的原因是因为有可能触发多个组件更新,就会有不同的watcher。对于我们开头页面的例子,因为只涉及一个组件,可以理解成 queue=[watcher]
到最后就会调用nextTick(flushSchedulerQueue),将flushSchedulerQueue当做回调函数传给nextTick去执行。flushSchedulerQueue方法就是个循环跑queue队列里面每个watcher的run方法,去触发每个组件的更新。
好了,其实上面就是想说明,响应式数据的改变会触发组件更新,在nextTick中会加入组件渲染的函数。所以回到最开始,callbacks数组现在就要变成这样的数据:callbacks = [用户自定义回调函数,组件渲染的函数]
然后等页面的同步代码data赋值走完,这个队列就会依次执行,就是上面说的Promise中的then回调执行。(右图就是callbacks队列里的方法依次执行)
所以回到最开始的问题,下图这个用户自定义回调函数是先执行的,而此时组件渲染的函数还没执行,所以页面打印出来时一开始还没变化渲染的值10。
如果此时我们调换下页面的代码顺序(下图),那打印的值就是300了。
因为此时callbacks = [组件渲染的函数,用户自定义回调函数] 。组件已经先渲染好了,才去调用户的自定义回调函数,所以打印的值就是300。
3.nextTick任务是宏任务还是微任务?
可能有人会直接简单认为就是微任务。但其实在Vue2中并不是那么准确。(注意是Vue2中,Vue3可以这样认为)。先说结论:我们可以说优先是微任务,在不支持Promise和MutationObserver的环境下就是宏任务。
Vue2源码中为了适配,采用了优雅降低的方式,就是我们前面说的timerFunc方法,它完整的代码如下。(由于太长,我截成两个图,第一个图是Promise最优先的)
从源码可以看出来,Vue2中会有个优雅降级的适配,是否能用Promise->是否能用MutationObserver->是否能用setImmediate->最后用setTimeout兜底。
所以才会得到上面的结论:优先是微任务,在不支持Promise和MutationObserver的环境下就是宏任务。
当然也要注意在Vue3中,源码是直接使用Promise处理,不再处理兼容问题。