nextTick
当你在 Vue 中更改响应式状态时,最终的 DOM 更新并不是同步生效的,而是由 Vue 将它们缓存在一个队列中,直到下一个“tick”才一起执行。这样是为了确保每个组件无论发生多少状态改变,都仅执行一次更新。
vue
<script setup>
import { nextTick, ref } 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>将回调推迟到下一个 DOM 更新周期之后执行。在更改了一些数据以等待 DOM 更新后,浏览器重新渲染之前立即使用它。
实现原理
ts
export function nextTick<T = void>(
this: T,
fn?: (this: T) => void,
): Promise<void> {
const p = currentFlushPromise || resolvedPromise
return fn ? p.then(this ? fn.bind(this) : fn) : p
}异步渲染
获取异步渲染后的宽高
vue
<script lang="ts" setup>
import axios from 'axios'
import { nextTick, ref } from 'vue'
interface User {
email: string
}
const userList = ref<User[]>([])
const listRef = ref()
const loading = ref(false)
async function fetchUser(params = { results: 10 }) {
loading.value = true
const response = await axios('https://randomuser.me/api', {
method: 'get',
params,
})
loading.value = false
return response.data.results
}
async function onFetchUserList() {
userList.value = await fetchUser()
console.log('nextTick: before', listRef.value.clientHeight)
await nextTick()
console.log('nextTick: after', listRef.value.clientHeight)
}
</script>
<template>
<n-button :loading="loading" @click="onFetchUserList">
获取用户列表
</n-button>
<ul ref="listRef">
<li v-for="user in userList" :key="user.email">
{{ user.email }}
</li>
</ul>
</template>