Vue2-$nextTick执行流程与实现原理

105 阅读2分钟

1# 定义

nextTick 是 Vue.js 提供的一个方法,用于在下次 DOM 更新循环结束之后执行延迟回调,使用 Vue.nextTick() 或者 this.$nextTick() 都是调用 nextTick() 这个方法 。在 Vue.js 中,当你修改数据后,DOM 不会立即更新,而是等待当前事件循环完成后才进行更新。如果你想要在 DOM 更新后执行一些操作,就可以使用 nextTick() 方法。

语法:function nextTick(callback?: () => void): Promise<void>

2# $nextTick 执行流程

$nextTick 的执行流程如下:

  1. 调用 $nextTick 方法: 当我们在 Vue 组件中调用 $nextTick 方法时,Vue 会将传入的回调函数添加到一个队列中。
  2. 等待当前任务完成: Vue 利用浏览器的异步机制来延迟执行这些回调函数,确保它们不会立即执行
  3. 等待事件循环完成: 在当前事件循环中,当所有同步代码执行完成后,浏览器会处理微任务队列
  4. 执行回调函数: Vue 将队列中的回调函数作为微任务添加到微任务队列中,确保在 DOM 更新后执行这些回调。
  5. 更新 DOM: 浏览器会继续执行微任务队列中的任务,其中包括 $nextTick 方法添加的回调函数。这样可以确保在 DOM 更新后执行相应的操作。
  6. 清空队列: 执行完微任务队列中的回调函数后,浏览器会重新渲染页面,并将更新后的 DOM 显示给用户。
  7. 异步更新: 这种异步更新的机制能够确保在正确的时机处理 DOM 更新后的操作,避免阻塞主线程而提高性能。

3# nextTick 实现原理

nextTick 实现原理是利用微任务(Microtask)或宏任务(Macrotask)机制,根据优先级选择合适的异步任务调度方式(如 PromiseMutationObserversetImmediatesetTimeout 等技术), 确保在当前更新循环结束后执行回调函数,从而在 DOM 更新后立即响应并获取最新状态。

  • callbacks:一个回调队列,用于存放异步执行的回调。
  • pending:一个异步锁标记位,避免将 timerFunc 重复推送到任务队列中去。
  • timerFunc:一个根据优先级选择合适的定时器函数,选择优先级为 promise.then > MutationObserver > setImmediate > setTimeout, 无论那种timerFunc 最终都会执行flushCallbacks 函数。
  • flushCallbacks : 负责执行 callbacks 中的回调函数,它会遍历回调函数队列并依次执行每个回调函数。
export function nextTick(cb?: Function, ctx?: Object) {
  let _resolve;
  // 若存在回调函数则直接放进回调队列 callbacks 中
  callbacks.push(() => {
    if (cb) {
      try {
        cb.call(ctx);
      } catch (e) {
        handleError(e, ctx, 'nextTick');
      }
    } else if (_resolve) {
      _resolve(ctx);
    }
  })
   // 异步锁 pending 为 true 表示正在执行回调函数
  if (!pending) {
    pending = true;
    timerFunc();
  }
  // 若没有提供回调函数但支持 Promise,则返回一个 Promise
  if (!cb && typeof Promise !== 'undefined') {
    return new Promise(resolve => {
      _resolve = resolve;
    })
  }
}