宏任务macro task: setTimeout、MessageChannel、postMessage、setImmediate
微任务micro task: MutationObsever 和 Promise.then
一般情况下微任务优先于宏任务执行;
在 Vue 2.4 之前的版本,nextTick 几乎都是基于 micro task 实现的,但由于 micro task 的执行优先级非常高,在某些场景下它甚至要比事件冒泡还要快,就会导致一些诡异的问题,如 issue #4521、#6690、#6566;但是如果全部都改成 macro task,对一些有重绘和动画的场景也会有性能影响,如 issue #6813。所以最终 nextTick 采取的策略是默认走 micro task,对于一些 DOM 交互事件,如 v-on 绑定的事件回调函数的处理,会强制走 macro task。
作者:ustbhuangyi
链接:https://www.imooc.com/article/21499
来源:Vue.js 升级踩坑小记
本文原创发布于慕课网 ,转载请注明出处,谢谢合作
Vue.js 2.5中$nextTick的MutationObserver 不见了,改成了 MessageChannel 的实现 (MutationObserver是HTML5中的新API,是个用来监视DOM变动的接口。他能监听一个DOM对象上发生的子节点删除、属性修改、文本内容修改等等。)
在 2.5 当中我们引入了一个改动,使得当一个 v-on DOM 事件侦听器触发更新时,会使用 Macrotask 而不是 Microtask 来进行异步缓冲。这原本是为了修正一类浏览器的特殊边际情况导致的 bug 才引入的,但这个改动本身却导致了更多其它的问题。在 2.6 里面我们对于原本的边际情况找到了更简单的 fix,因此这个 Macrotask 的改动也就没有必要了。现在 nextTick 将会统一全部使用 Microtask。
https://zhuanlan.zhihu.com/p/56260917
vue中 $nextTick源码分析 :https://ustbhuangyi.github.io/vue-analysis/reactive/next-tick.html#vue-%E7%9A%84%E5%AE%9E%E7%8E%B0
一、例子1
<div>
<ul>
<li class="item" v-for="item in testItems">
{{item}}
</li>
</ul>
</div>
<script>
data() {
return {
testItems: [
'a',
'b',
'c',
]
}
},
mounted() {
this.testItems.push('我是新增的项目');
console.log('this.testItems.length', this.testItems.length);
console.log('mounted里的 this.$el.querySelectorAll(\'.item\').length', this.$el.querySelectorAll('.item').length);
this.$nextTick(() => {
console.log('mounted里的$nextTick的 this.$el.querySelectorAll(\'.item\').length', this.$el.querySelectorAll('.item').length);
})
},
updated() {
console.log(' updated 里的this.$el.querySelectorAll(\'.item\').length', this.$el.querySelectorAll('.item').length)
},
</script>
上面的运行结果

当然页面上也正常展示
'a',
'b',
'c',
'我是新增的项目'
这四个内容。
可以看到 mounted里的 this.$el.querySelectorAll('.item').length是3 而不是4,但是我们采用了$nextTick后就正常了;原因是
Vue 是异步执行 DOM 更新,为了在数据变化之后等待 Vue 完成更新 DOM ,可以在数据变化之后立即使用 Vue.nextTick(callback) 。这样回调函数在 DOM 更新完成后就会调用。
具体大家可以看这里 https://cn.vuejs.org/v2/guide/reactivity.html#%E5%BC%82%E6%AD%A5%E6%9B%B4%E6%96%B0%E9%98%9F%E5%88%97
二、例子2
前提条件 vue版本 "vue": "^2.5.2",
<template>
<div ref="msgDiv">{{msg}}</div>
<div>Message got outside $nextTick: {{msg1}}</div>
<div>Message got inside $nextTick: {{msg2}}</div>
<div>Message got outside $nextTick: {{msg3}}</div>
<button @click="changeMsg1">
Change the Message1
</button>
<div ref="msgDiva">{{msga}}</div>
<div>Message got outside $nextTick: {{msg1a}}</div>
<div>Message got inside $nextTick: {{msg2a}}</div>
<div>Message got outside $nextTick: {{msg3a}}</div>
<button @click="changeMsg2">
Change the Message2
</button>
</template>
data(){
return{
msg: 'Hello Vue.',
msg1: '',
msg2: '',
msg3: '',
msga: 'Hello Vueaaaa.',
msg1a: '',
msg2a: '',
msg3a: '',
}
},
methods:{
changeMsg1() {
this.msg = 'Hello world.';
this.msg1 = this.$refs.msgDiv.innerHTML
// Hello Vue 因为vue考虑到性能虽然数据层更新了 ,DOM不会立即更新
this.$nextTick(() => {
this.msg2 = this.$refs.msgDiv.innerHTML
// Hello world $nextTick中的函数会在DOM更新完后立即执行 $nextTick 在vue2.5及以后属于宏任务了
});
this.msg3 = this.$refs.msgDiv.innerHTML
// Hello Vue 因为vue考虑到性能虽然数据层更新了 ,DOM不会立即更新
},
changeMsg2() {
this.msg1a = this.$refs.msgDiva.innerHTML
// Hello Vueaaaa.
this.$nextTick(() => {
this.msg2a = this.$refs.msgDiva.innerHTML
// Hello worlda $nextTick中的函数会在DOM更新完后立即执行 $nextTick 在vue2.5及以后属于宏任务了
});
this.msg3a = this.$refs.msgDiva.innerHTML
// Hello Vueaaaa.
this.msga = 'Hello worlda';
},
}
参考例子: https://zhuanlan.zhihu.com/p/26724001
三、应用场景
1、想在created里进行dom操作
2、想dom变化以后进行相应的操作
简单理解Vue中的nextTick