Q8
★ ★ ★ ★ ★
Vue 3 是如何处理异步更新队列的?
⚡ 速记答案(30 秒)
状态变化时不立刻更新 DOM,而是把相关副作用/组件放入队列并打标为"脏"。使用微任务在同一 tick 批量 flush 队列。
📖 详细讲解
标准面试回答(推荐记住)
Vue 3 采用异步更新策略。当响应式数据变化时,不会立即更新 DOM,而是将更新任务放入队列并标记为脏。然后使用 Promise.then 等微任务机制,在同一个事件循环中批量处理队列。这样可以合并多次修改,避免重复渲染,且保证父组件先于子组件更新。
异步更新调度机制
核心流程:
- 触发更新:响应式数据变更触发 Setter -> Trigger -> Effect Scheduler。
- 入队 (queueJob):将 Effect 任务推入全局
queue数组,并通过 IDs 去重(防止同一组件多次更新)。 - 微任务 (Microtask):通过
Promise.resolve().then(flushJobs)启动微任务。 - 刷新队列 (flushJobs):
- 对队列进行排序(保证父组件先于子组件更新,因为父组件 ID 小)。
- 依次执行 Effect.run() 进行 Patch。
- 执行生命周期钩子(updated)和 watch 回调。
为什么是微任务?
微任务在当前宏任务结束后、渲染前立即执行,保证在一次事件循环中完成所有数据变更,合并 DOM 操作,性能最优。
✅ 面试要点
- •理解异步更新的性能优势
- •知道更新队列的执行时机
- •掌握 nextTick 的使用场景
💻 代码示例
异步更新与 nextTick
import { ref, nextTick } from 'vue'
const count = ref(0)
// 同步修改多次
count.value++
count.value++
count.value++
// 实际只会触发一次 DOM 更新
// 需要等待 DOM 更新完成
async function updateAndRead() {
count.value = 100
// 此时 DOM 还没更新
console.log(document.querySelector('#count')?.textContent) // 旧值
await nextTick()
// DOM 已更新
console.log(document.querySelector('#count')?.textContent) // 100
}