使用
在双向绑定中,看如下代码:
vm.data1 = 'hello'
vm.$el.textContent //此时并不会立即得到data1的值
这是因为vue并不会立即更新DOM,而Vue.nextTick方法可以实现在dom更新后所需要做的事情。
vm.data1 = 'hello'
Vue.nextTick(function(){
console.log(vm.$el.textContent) // hello
})
原理
看了源码发现,nextTick实际就是创建一个新的宏任务(或微任务)。因为每个宏任务执行完后浏览器都需要重新渲染界面。当然,这不是主要的原因。
主要的原因是因为vue中双向绑定的数据在赋值后需要调用watcher的update方法(比如某个div绑定了一个数据,那么该div就会生成一个watcher,而update的方法就是更新自身的innerHTML了,当然这中间还有一层虚拟dom),而watcher的update也是通过nextTick来执行的。
也就是当vm.xx = 'xxx’的时候,watcher的update方法添加到了宏任务中,此时如果想要拿到更新后的dom,就可以再使用nextTick往末尾添加一个新任务,等前一个dom watcher执行完后就会轮到我们的nextTick,那个时候也就拿到了更新后的dom了。
nextTick使用宏任务还是微任务?
nextTick默认是使用的微任务,在看源码时有一段注释:
Here we have async deferring wrappers using both microtasks and (macro) tasks.
In < 2.4 we used microtasks everywhere, but there are some scenarios where
microtasks have too high a priority and fire in between supposedly
sequential events (e.g. #4521, #6690) or even between bubbling of the same
event (#6566). However, using (macro) tasks everywhere also has subtle problems
when state is changed right before repaint (e.g. #6813, out-in transitions).
Here we use microtask by default, but expose a way to force (macro) task when
needed (e.g. in event handlers attached by v-on).
翻译:
这里我们使用微任务和(宏)任务来实现异步延迟封装器。
在<2.4中,我们在任何地方都使用了微任务,但是在某些情况下,
微任务具有太高的优先级,并且在所谓的顺序事件(例如#4521,#6690)之间或甚至在同一事件的冒泡之间(#6566)之间发生冲突。
但是,在重新绘制之前状态更改时(例如#6813,out-in过渡),在任何地方使用(宏)任务也会产生细微的问题。
这里我们默认使用microtask,但在需要时(例如在v-on附加的事件处理程序中)提供强制(宏)任务的方法。
我在对data进行赋新值(如vm.xx = ‘xxx’)的调试过程中,发现nextTick使用的是宏任务的MessageChannel。
通过案例讲解流程
<div id="app">
<div id="d1">{{ msg1 }}</div>
<div id="d2">{{ obj.p1 }}</div>
<button @click="mClick" value="btn">btn</button>
</div>
let vm = new Vue({
el: '#app',
data () {
return {
msg1: 'old',
obj: {
p1: 'old'
}
}
},
methods: {
mClick () {
this.msg1 = 'new'
Vue.nextTick(function () {
let d1 = document.getElementById("d1").innerHTML // new
let d2 = document.getElementById("d2").innerHTML // new
})
this.obj.p1 = 'new'
}
}
})
在上方代码中,view是一个watcher,在this.msg1 = 'new'
时就已经将该watcher加入了执行队列中,而该watcher的作用就是将view中的绑定内容替换成data的具体值。而在this.obj.p1 = 'new'
时并不会重复添加watcher,会直接过滤掉。还有,在data里会生成两个observer对象,一个是data,一个是data.obj,因为该值是对象。