官方文档表示:为了在数据变化之后等待Vue完成更新DOM,可以在数据变化之后立即执行Vue.$nextTick(callback)
,这样回调函数就可以在数据变化之后立即执行。
这段话的意思是:
例如:存在
<div id="test">{{ message }}</div> // 假如此时message的值是 hi
当我们对data(){return{}}
中的值进行赋值修改时(例:this.message = 'hello'
),虽然数据变化了,但是其实DOM上的内容并未改变,所以如果此时通过原生js获取这个div的innerHTML时,它的值仍是'hi',而不是赋值修改后的'hello'。
this.message = 'hello';
console.log(this.message); // hello, 数据发生了变化
alert(document.getElementById('test').innerHTML); // hi, DOM还未发生变化
而如果我们在修改了数据之后就想要马上对变化更新后的DOM进行操作,就需要在this.$nextTick(callback)
中调用:
this.message = 'hello';
console.log(this.message); // hello, 数据发生了变化
this.$nextTick(() => {
alert(document.getElementById('test').innerHTML); // hello, DOM已发生变化
})
这个例子可能看起来没什么实际用途,另一个例子:
当我们需要通过某个值(假设: show_login_register
)来对显示的内容(是显示登录框还是注册框)进行判断(v-if,v-else
),并需要获取变化后的内容的clientHeight
来做定位设置,此时,如果我们在show_login_register
的值被赋值改变后就直接获取内容的clientHeight
,此时,内容还是原来的登录框(假设是想要从登录框变成注册框),如果我们想要获取到变化后的内容(注册框的clientHeight
),就需要在this.$nextTick
的回调函数中做操作。
实现原理:
Vue其实是异步执行DOM更新的。
1、只要观察到数据变化,Vue将开启一个队列,并对同一事件循环中发生的所有数据变化做缓冲。
2、如果同一个watcher被多次触发(即一个变量在一次事件循环中被赋值变化了多次),则只会推入队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和DOM操作非常重要。
3、然后,在下一个的事件循环"tick"中,Vue刷新队列并执行实际(已去重的)工作(即更新DOM)。
Vue在内部尝试对异步队列使用原生的Promise.then
和MutationObserver
(这个是html 5新加的一个功能,其功能是监听dom节点的变动,在所有dom变动完成后,执行回调函数),如果执行环境不支持,会采用setTimeout(fn,0)
代替。