外观
nexttick
约 863 字大约 3 分钟
2025-10-15
8.nexttick
vue的页面渲染-异步更新
vue中的页面渲染实际是异步渲染的——为什么需要异步渲染呢?
如果是同步渲染的话,修改一次数据就会进行一次页面的数据更新,假设短时间内我对同一个数据修改了多次,最终页面中显示的数据实际只有最后修改后的值,但是中间更新过程中也执行了多次页面更新代码,这之间的计算实际用户是不可见的,并没有必要。
而异步更新,vue中会把同一事件循环中所有的数据变更 收集起来,只更新一次 DOM。
异步更新实现原理
Vue 2:基于 Watcher 的异步更新机制
在 Vue 2 中,组件的渲染与数据变动更新是通过 Watcher 完成的。
每个响应式数据都会对应若干个 Watcher(包括渲染 Watcher 和用户定义的 Watcher)。
1. 异步队列机制:当数据发生变化时,Vue 不会立刻执行 DOM 更新,而是将对应的 Watcher 推入一个 异步更新队列(queue
)中 2. 去重机制:为了避免同一个 Watcher 在一个事件循环中被多次执行(例如连续修改同一个数据),Vue 通过一个 has
哈希表来去重 3. 异步调度Vue 使用 nextTick
异步地在当前事件循环结束后(宏任务或微任务)统一执行队列中的所有 Watcher 4. 执行过程
- 数据变化 →
dep.notify()
- 收集到相关的 Watcher → 推入异步队列
- 在下一次事件循环中执行
flushSchedulerQueue()
- 所有 Watcher 执行更新逻辑(触发重新渲染)
Vue 3:基于 effect 的异步更新机制
在 Vue 3 中,响应式系统完全重写,使用 Proxy + effect(副作用函数) 来实现依赖追踪和更新。
1. effect 依赖收集 每当在 effect()
中访问响应式数据时,Vue 会自动进行依赖收集:内部会把当前的 effect 函数与依赖的响应式对象关联起来。
effect(() => {
// 访问响应式数据
console.log(state.count)
})
2. 异步调度器 (scheduler):当响应式数据变化时,不会立即执行 effect,而是通过调度器(scheduler
)来异步调度执行:
effect(() => { /* 更新视图 */ }, {
scheduler: queueJob
})
其中 queueJob
的作用类似于 Vue2 的异步更新队列,内部实现:
- 将 job(即 effect)加入任务队列;
- 去重相同的 job;
- 使用微任务(
Promise.then
)在下一次事件循环中批量执行。 3. 异步队列执行:Vue3 内部的queueJob()
和flushJobs()
实现与 Vue2 类似:
总结对比
特性 | Vue 2 | Vue 3 |
---|---|---|
响应式核心 | Object.defineProperty + Watcher | Proxy + effect |
更新调度 | 异步队列(Watcher 队列) | 异步队列(Job 队列) |
去重机制 | 通过 has 标记去重 | 通过 Set 自动去重 |
执行时机 | nextTick (微任务/宏任务) | queueJob (微任务) |
实现方式 | Watcher 收集依赖 | effect 收集依赖 |
更新策略 | 多次修改 → 合并成一次更新 | 多次修改 → 合并成一次更新 |
nexttick实现
vue也提供了nexttick api给开发者使用,可以在页面数据更新后获取更新后的数据,用来在 DOM 更新完成后 执行回调。
选择异步任务的优先级是:如果不支持则采用下一种方式实现 Promise.then
->MutationObserver
->setImmediate
->setTimeout