1. 作用与用法
当你在 Vue 中更改响应式状态时,最终的 DOM 更新并不是同步生效的,而是由 Vue 将它们(DOM 更新操作)缓存在一个队列中,直到下一个 “tick” 才一起执行。这样是为了确保每个组件无论发生多少状态改变,都仅执行一次状态更新。
nextTick() 可以在状态改变后立即使用,以等待 DOM 更新完成。你可以传递一个回调函数作为参数,或者 await 返回的 Promise。
<script setup>
import { ref, nextTick } from 'vue'
const count = ref(0)
async function increment() {
count.value++
// DOM 还未更新
console.log(document.getElementById('counter').textContent) // 0
await nextTick()
// DOM 此时已经更新
console.log(document.getElementById('counter').textContent) // 1
}
</script>
<template>
<button id="counter" @click="increment">{{ count }}</button>
</template>
那么浏览器在一个 “tick” 里做了什么?
1. 处理用户的事件,就是 event,例如 click,input,change等
2. 执行定时器任务
3. 执行 requestAnimationFrame
4. 执行 DOM 的回流与重绘
5. 计算更新图层的绘制指令
6. 绘制指令合并主线程,如果有空余时间会执行 requestidlecallback
2. 案例
聊天框的滚动条在发送消息后自动回底
<template>
<div class="wrap">
<div class="chat" ref="chatBox">
<p v-for="msg in chatList">{{ msg }}</p>
</div>
<div class="send">
<textarea type="text" v-model="myMsg" rows="5"/>
<button @click="send">发送</button>
</div>
</div>
</template>
<script setup lang="ts">import { nextTick, reactive, ref } from 'vue';
let chatList = reactive(["我是 lzy"])
let myMsg = ref("")
let chatBox = ref()
const send = async () => {
if(myMsg.value === "") return alert("请输入内容!")
chatList.push(myMsg.value)
// 调用 nextTick 来传递回调,等 DOM 更新后再执行回调里面的代码
nextTick(() => {
chatBox.value.scrollTop = 999
})
// 或者这样写
// await nextTick()
// chatBox.value.scrollTop = 999
}
</script>
<style scoped>
.wrap {
border: 1px solid #ccc;
width: fit-content;
}
.chat{
width: 400px;
height: 500px;
box-sizing: border-box;
padding: 20px;
overflow: auto;
}
.chat p {
border: 1px solid skyblue;
width: fit-content;
padding: 10px;
border-radius: 5px;
max-width: 300px;
word-wrap: break-word;
}
.send{
border: 1px solid #ccc;
width: 400px;
height: 200px;
position: relative;
}
.send textarea {
border: none;
outline: none;
display: block;
width: 400px;
box-sizing: border-box;
padding: 10px;
font-size: 20px;
resize: none;
}
.send button {
position: absolute;
bottom: 10px;
right: 10px;
outline: none;
border: 1px solid #ccc;
padding: 10px 30px;
border-radius: 5px;
cursor: pointer;
}
</style>
可以这样理解,如果先设置了滚动条的 scrollTop,那么 DOM 更新后,滚动条就可能变短(因为消息多了,而聊天框的总高度一定,所以滚动条长度变短),而 scrollTop 没有再次设置,这时候页面呈现的效果就是滚动条没有在底部。
使用 nextTick 之后,我们先等页面更新,此时聊天框的高度已经确定了,再执行 nextTick 的回调来设置滚动条的 scrollTop,这时候页面呈现的效果就是滚动条到达底部。