Vue 源码学习 (5) - nextTick
写在前面
在平时开发中,我们更新数据后,需要通过 nextTick 的回调中才能获取 DOM。
this.value = 1 this.$nextTick(() => { ... })
通过前面的文章我们知道,这是因为 Vue 中数据更新后,视图并不是立即更新,而是进入批量更新的队列中,而批量更新的队列也是在 nextTick 的回调,所以当 更新的回调执行完成后,我们操作 dom 的回调执行时才能正确获取到 DOM 的状态。
所以来看看 nextTick
nextTick
export function nextTick(cb?: (...args: any[]) => any, ctx?: object) { let _resolve callbacks.push(() => { if (cb) { cb.call(ctx) } else if (_resolve) { _resolve(ctx) } }) if (!pending) { pending = true timerFunc() } if (!cb && typeof Promise !== 'undefined') { return new Promise(resolve => { _resolve = resolve }) } }
nextTick 方法可以将 回调函数推入到 callbacks 数组中,如果未传入 cb, 则会执行 resolve, 保证 nextTick 后的 then 回调被执行。
这里会执行 timeFunc 函数
const p = Promise.resolve() timerFunc = () => { p.then(flushCallbacks) if (isIOS) setTimeout(noop) } ... timerFunc = () => { setImmediate(flushCallbacks) } ... timerFunc = () => { setTimeout(flushCallbacks, 0) }
这里做了很多兼容,有使用 Promise 的微任务回调版本,也有定时器版本的宏任务版本。
这里保证 nextTick 回调的执行实际在 任务队列中完成,执行 flushCallbacks
function flushCallbacks() { pending = false const copies = callbacks.slice(0) callbacks.length = 0 for (let i = 0; i < copies.length; i++) { copies[i]() } }
平时我们使用的 $nextTick 就是 这个逻辑
Vue.nextTick = nextTick Vue.prototype.$nextTick = function (fn: (...args: any[]) => any) { return nextTick(fn, this) }
总结
通过对 nextTick 的源码学习,我们知道了 Vue 的批量更新是 不是在主线程执行的逻辑里面,而是在任务队列中执行相关的逻辑。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论