Vue中nextTick
一、nextTick是什么?
官方定义:
在下次DOM更新循环结束之后执行延迟回调。在修改数据之后立即执行这个方法,获取更新后的DOM。
我们可以理解成Vue在更新DOM时是异步执行的。当数据发生改变时,Vue将开启一个异步更新队列,视图需要等所有数据改变完之后,再统一进行更新。
当数据更新了,在DOM中渲染后,自动执行该函数。
代码示例
<template>
<div class="app">
<div ref="msgDiv">{{msg}}</div>
<div v-if="msg1">{{msg1}}</div>
<div v-if="msg2">{{msg2}}</div>
<div v-if="msg3">{{msg3}}</div>
<button @click="changeMsg">点击我</button>
</div>
</template>
<script>
export default {
name: 'vueDemo',
data() {
return {
msg: 'hello, my brother',
msg1: '',
msg2: '',
msg3: '',
}
},
methods: {
changeMsg() {
this.msg = 'hello, my Sean'
this.msg1 = this.$refs.msgDiv.innerHTML
this.$nextTick(() => {
this.msg2 = this.$refs.msgDiv.innerHTML
})
this.msg3 = this.$refs.msgDiv.innerHTML
}
}
}
</script>
点击前
点击后
原因是DOM的更新时异步的,msg1和msg3还是之前的数据,而msg2显示的是DOM更新过后的数据。
二、使用场景
- 在Vue生命周期的
created()
钩子函数进行的DOM
操作一定要放在Vue.nextTick()
的回调函数中。
在created()
钩子函数执行的时候,DOM
并没有进行任何渲染,而此时进行DOM操作是徒劳的,所以这个时候一定要将执行DOM操作的js代码放入this.nextTick()
的回调函数中。而mounted()
钩子函数执行时,所有DOM的挂载和渲染都已完成,此时可以在该钩子函数中进行DOM操作。 - 在数据变化后要执行的某个操作,需要使用随数据的改变而改变的DOM结构时,这个操作应该放入
this.nextTick()
中。
Vue异步执行DOM更新,只要观察到数据变化,Vue将开启一个队列,并缓冲在同一事件循环中发生的所有数据改变。如果同一个watcher被多次处罚,最会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和DOM操作上非常重要。
然后,在下一个时间循环“tick”中,Vue刷新队列并执行实际(已经去重的)工作。Vue在内部尝试对队列使用原生的Promise.then
和MessageChannel
,如果执行环境不支持,会采用setTimeout(fn, 0)
代替。
事件循环
第一个tick(图中的第一个步骤,即“本次更新循环”):
- 首先修改数据,这是同步任务。同一事件循环的所有同步任务都在主线程上执行,形成一个执行栈,此时还未涉及DOM。
- Vue开启一个异步队列,并缓冲在此事件循环中所有的数据改变。如果同一个watcher被多次触发,只会被推入到队列中一次。
第二个tick(图中第二个步骤,即“下次更新循环”):
同步任务执行完毕,开始执行异步watcher队列的任务,更新DOM。Vue在内部尝试对异步队列使用原生的Promise.then和MessageChannel方法,如果执行环境不支持,会采用setTimeout(fn, 0)代替。
第三个tick(图中的第三个步骤)
在下次DOM更新循环结束之后,通过Vue.nextTick获取到改变后的DOM。通过setTimeout(fn, 0)也可以获取到。
异步是单独的一个tick,不会和同步发生在一个tick里,这也是DOM不会马上改变的原因。
参考链接:Vue.nextTick的原理和用途