设想一个场景,使用v-if实现div的隐藏和显示,并且在显示时获取div里面的内容,div默认不显示,代码如下:
html:
这是一段文本
js:
var app = new Vue({
el: "#app",
data: {
showDemo: false
},
methods: {
getText: function () {
this.showDemo = true;
var txt = document.getElementById('demo').innerHTML;
console.log(txt)
}
}
})
这段代码在运行时会在控制台上抛出一个错误,Cannot read property 'innerHTML' of null.意思是获取不到div元素,这是就涉及到Vue的一个重要概念:异步更新队列.
Vue在观察到数据变化时,并不是直接更新DOM,而是开启一个队列,并缓冲在同一事件循环中发生的所有数据改变.在缓冲时会去除重复数据,从而避免不必要的计算和DOM操作.然后,在下一个事件tick中,Vue刷新队列并执行实际工作(已去重),所以如果你用一个for循环来动态的改变数据100次,其实它只会用最后一个的改变,如果没有这种机制,DOM需要重绘100次,这个开销非常大.
Vue会根据当前的浏览器环境优先使用原生的Promise.then和MutationObserver,如果都不支持,就回采用setTimeout代替.
知道了Vue异步更新DOM的原理,上面示例报错也就不难理解了.实际上,当this.showDemo=true时,div还没有被创建.知道下一个Vue事件循环时,才开始创建.
$nextTick就是用来知道什么时候DOM更新完成的,所以可修正上面的js代码如下:
var app = new Vue({
el: "#app",
data: {
showDemo: false
},
methods: {
getText: function () {
this.showDemo = true;
this.$nextTick(function () {
var txt = document.getElementById('demo').innerHTML;
console.log(txt)
})
}
}
})
这时再点击按钮,控制台就打印出div的内容"这是一段文本"了.
理论上我们不应该主动操作DOM,因为Vue的核心思想就是数据驱动DOM,但是在很多业务里,我们避免不了使用一些第三方库如swiper.js,propper.js,这些基于原生Javascript的库都有创建和更新及销毁的完整生命周期,与Vue配合使用时,就要利用好$nextTick.