异步更新队列
Vue 在检测到数据变化后,不会立即更新 DOM,而是将更新操作放入异步队列,在下一个事件循环中批量执行。
为什么需要异步更新
JavaScript
// 同步更新的问题
this.count = 1
this.count = 2
this.count = 3
// 如果每次变化都更新DOM,会触发3次渲染
Vue 将多次数据变化合并为一次 DOM 更新,提升性能。
nextTick 实现
JavaScript
const callbacks = []
let pending = false
function flushCallbacks() {
pending = false
const copies = callbacks.slice(0)
callbacks.length = 0
for (let i = 0; i < copies.length; i++) {
copies[i]()
}
}
// 优先使用微任务(Promise/MutationObserver)
let timerFunc
if (typeof Promise !== 'undefined') {
const p = Promise.resolve()
timerFunc = () => p.then(flushCallbacks)
} else {
// 降级为宏任务 setTimeout
timerFunc = () => setTimeout(flushCallbacks, 0)
}
function nextTick(cb) {
callbacks.push(cb)
if (!pending) {
pending = true
timerFunc()
}
}
Watcher 的异步更新
JavaScript
class Watcher {
update() {
// 将更新放入队列
queueWatcher(this)
}
}
const queue = []
const has = {}
function queueWatcher(watcher) {
const id = watcher.id
if (has[id] == null) {
has[id] = true
queue.push(watcher)
// 下一个tick统一执行
nextTick(flushSchedulerQueue)
}
}
function flushSchedulerQueue() {
// 按优先级排序
queue.sort((a, b) => a.id - b.id)
for (let i = 0; i < queue.length; i++) {
queue[i].run()
}
queue.length = 0
}
使用 nextTick
JavaScript
// 场景:数据更新后立即操作DOM
this.message = 'updated'
// 此时DOM还未更新
console.log(this.$el.textContent) // 旧值
this.$nextTick(() => {
console.log(this.$el.textContent) // 新值
})
// 或使用 async/await
await this.$nextTick()
console.log(this.$el.textContent)
Vue 3 的 flushQueue
JavaScript
import { nextTick, ref } from 'vue'
const count = ref(0)
function increment() {
count.value++
// DOM 还未更新
nextTick(() => {
// DOM 已更新
})
}
事件循环中的执行顺序
text
同步代码执行 → 微任务执行(Promise.then)→ DOM更新 → 宏任务执行(setTimeout)
↑ ↑
this.count = 1 nextTick 回调
要点总结
- Vue 将DOM更新放入异步队列,批量执行避免重复渲染
- 优先使用微任务(Promise),降级使用宏任务(setTimeout)
- 多个相同 Watcher 的更新会被去重合并
$nextTick用于获取更新后的DOM状态
📝 发现内容有误?点击此处直接编辑