Appearance
nextTick
当你在 Vue 中更改响应式状态时,最终的 DOM 更新并不是同步生效的,而是由 Vue 将它们缓存在一个队列中,直到下一个 "tick" 才一起执行。这样是为了确保每个组件无论发生多少状态改变,都仅执行一次更新。
0
<template>
<div ref="countRef">{{ count }}</div>
<button @click="increase">increase</button>
</template>
<script lang="ts" setup>
import { ref, onUpdated } from 'vue'
const countRef = ref()
const count = ref(0)
const increase = () => {
count.value++
// console.log('instance: ', countRef.value.innerText)
for (let i = 0; i < 1000; i++) {
count.value = i
}
}
onUpdated(() => {
console.log('update')
})
</script>
将回调推迟到下一个 DOM 更新周期之后执行。在更改了一些数据以等待 DOM 更新后,浏览器重新渲染之前立即使用它。
Vue 在更新 DOM 时是异步执行的。DOM 节点更新成功后调用
import { createApp, nextTick } from 'vue'
const app = createApp({
setup() {
const message = ref('Hello!')
const changeMessage = async newMessage => {
message.value = newMessage
await nextTick()
console.log('Now DOM is updated')
}
}
})
实现原理
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
}
异步渲染
获取异步渲染后的宽高
<template>
<n-button @click="onFetchUserList" :loading="loading">获取用户列表</n-button>
<ul ref="listRef">
<li v-for="user in userList">
{{ user.email }}
</li>
</ul>
</template>
<script lang="ts" setup>
import { ref, nextTick } from 'vue'
import axios from 'axios'
type User = {
email: string
}
const userList = ref<User[]>([])
const listRef = ref()
const loading = ref(false)
const fetchUser = async (params = { results: 10 }) => {
loading.value = true
const response = await axios('https://randomuser.me/api', {
method: 'get',
params
})
loading.value = false
return response.data.results
}
const onFetchUserList = async () => {
userList.value = await fetchUser()
console.log('nextTick: before', listRef.value.clientHeight)
await nextTick()
console.log('nextTick: after', listRef.value.clientHeight)
}
</script>