nextTick 原理
为什么修改数据后不能立刻获取 DOM?
背景:事件循环
JavaScript 执行是单线程的,靠事件循环驱动:
每执行完一个宏任务,会清空所有微任务,然后再执行下一个宏任务。
Vue 更新是异步的
当你修改响应式数据时,Vue 不会立刻更新 DOM,而是:
- 触发 setter
- 触发 watcher
- 把"需要更新"这个任务放进异步队列
同一个事件循环中的多次修改会合并,这是 Vue 的性能优化策略。
问题:修改后立刻获取 DOM,拿到的还是旧的。
nextTick 干什么用?
等 DOM 更新完成后,再执行回调:
Vue 3 用法
原理详解
核心实现
关键点:
- 所有回调存在
callbacks数组 - 用
pending标记确保同一时间只有一个 Promise 在等待 - 回调通过微任务执行,在 DOM 更新之后
Vue 2 降级策略
Vue 2 还需要兼容旧浏览器:
Vue 3
直接用 Promise.resolve(),不再考虑 IE 兼容性,代码更简洁。
异步更新队列
Vue 内部维护一个 watcher 队列:
为什么要用队列?
- 如果每个修改都立即更新 DOM,性能很差
- 队列机制保证 同一事件循环内只更新一次
- 这就是 Vue 的 批处理(batching)
场景
1. 操作更新后的 DOM
2. 修改数据后获取元素尺寸
3. 父子组件通信
4. 表单验证
nextTick、单 rAF 和双 rAF 的区别
三者解决的不是同一个问题:
浏览器一帧可以简化为:
nextTick:等 Vue 更新 DOM
这时 DOM 已就绪,但 JavaScript 可能仍处于当前任务后的微任务阶段,浏览器还没有机会 Paint。
单 rAF:在下一帧绘制前执行
时序大致是:
如果 startHeavyWork() 是长任务,它会占用主线程,后面的 Paint 仍然要等它结束。所以单 rAF 不能保证 Loading 已经被用户看到。
双 rAF:在两次回调之间留出一帧
时序更接近:
两个 rAF 必须嵌套注册,或像上面一样连续 await。下面的写法会把两个回调放入同一帧,不等于双 rAF:
双 rAF 也不是严格的 Paint 完成 API。页面处于后台、主线程被阻塞或 WebView 主动跳帧时,rAF 会被暂停或延迟。它只是给浏览器留出绘制机会的工程技巧。
如何选择
注意事项
1. 不要在 nextTick 里再修改数据
2. 连续多个 nextTick
总结
- Vue 更新是异步的,会合并同一事件循环内的多次修改
nextTick利用微任务(Promise),在 DOM 更新后执行回调- Vue 3 直接用 Promise,Vue 2 有降级策略
- 适用场景:获取更新后的 DOM、聚焦、滚动、第三方库集成

