vue.$nextTick
定义:在下次dom更新循环结束之后执行延迟回调。
理解:可以在这个方法中获取更新后的dom。
<template>
<button ref="btn" @click="add">count: {{count}}</button>
</template>
<script>
export default {
data(){
return {
count: 0,
}
},
methods:{
add(){
this.count ++;
console.log(this.count, this.$refs.btn.innerText);// 1 "count: 0"
this.$nextTick(()=>{
console.log(this.$refs.btn.innerText)// count: 1
})
}
}
}
</script>
上面例子中可以看到,更新count之后立即打印dom中的内容,是更新之前的,而nextTick中可以获取到更新后的dom。
原理:
vue是异步执行dom更新的,一旦检测到数据变化,vue就会开启一个队列,然后把在同一事件循环中观察到数据变化的watcher推送进这个队列。如果这个watcher被触发多次,只会被推送到队列一次。这种缓冲行为可以有效去掉重复数据造成的不必要的计算和dom操作,比如
<template>
<div>{{count}}</div>
</template>
<script>
export default{
data(){
return {
count: 0
}
},
created(){
for(let i = 0; i < 10000; i++){
this.count ++
}
}
}
</script>
上面例子中,created时count会执行10000次++操作,每次++都会依次触发 setter-dep-watcher-update-patch,如果没有异步更新视图,那么每一次++操作都会更新dom,非常消耗性能。
所以vue实现了一个queue队列,在下一个事件循环时,会清空队列,并且更新dom。同时,有用相同id的watcher不会被重复添加到队列中,所以不会执行10000次watcher的count
使用场景:
需要操作因数据而改变的dom。
实现下图的对话窗口页时,因为无法提前知道接口返回html片段内容的高度,所以需要在消息列表更新dom之后,根据高度判断是否需要折叠和显示折叠按钮,这时就可以用到vue.nextTick
this.$nextTick(()=>{
let lastIndex = this.QAlist.length / 2 - 1;
let par_msg = document.getElementsByClassName("service-chat-content")[
this.QAlist.length
];
let height = par_msg.getElementsByClassName("msg-content")[0]
.offsetHeight;
if (height > 400) {
par_msg.classList.add("collapse");
let open = document.createElement("DIV");
open.innerText = "展开";
open.classList.add("open");
open.addEventListener("click", e => {
if (open.innerText === "展开") {
open.innerText = "收起";
par_msg.style.height = "auto";
} else {
open.innerText = "展开";
par_msg.style.height = "400px";
}
});
par_msg.appendChild(open);
}
}
});