Skip to content
本页目录

nextTick

在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM

返回值

$nextTick() 方法返回一个 Promise 对象

当数据发生变化的时候,vue不会立刻更新dom,而是开启一个队列把更新函数放入队列中,然后刷新队列,这里是使用了promise,就是当同步任务完成后才刷新队列然后执行里面的更新函数。nextTick就是获取刚才的promise,然后用then方法执行nextTick的回调函数。就是在dom更新完成后才会调用nextTick的回调函数。

为什么 Vue 使用异步更新队列?

为了提升性能与效率。
每次数据变化都会触发 watcher 通知,从而重新渲染相关 dom,异步方式可以将需要更新的 watcher 收集起来,如果重复,则不会被再次收集,

源码

  • callbacks: 用来存储用户注册的回调函数(获得了更新后 DOM 所进行的操作)
  • pending: 用来标记是否向任务队列添加任务,pendingfalse ,表示任务队列没有 nextTIck 任务,需要添加 nextTick 任务,当添加一个 nextTick 任务时,pendingture,在回调执行之前还有 nextTick 时,并不会重复添加任务到任务队列,当回调函数开始执行时,pendingflase,进行新的一轮事件循环。
  • flushCallbacks: 就是我们所说的被注册在任务队列中的任务,当这个函数执行,callbacks 中所有函数依次执行,然后清空 callbacks,并重置 pendingfalse,所以说,一轮事件循环中,flushCallbacks 只会执行一次。
  • microTimerFunc: 它的作用就是使用 Promise.thenflushCallbacks 添加到微任务队列中。
JS
var timerFunc;

// 优先使用 promise 方法
if (typeof Promise !== 'undefined' && isNative(Promise)) {
    var p = Promise.resolve();
    timerFunc = function () {
        p.then(flushCallbacks);

        // ios 系统兼容
        if (isIOS) { setTimeout(noop); }
    };
    isUsingMicroTask = true;

// 降级使用 MutationObserver 方法
} else if (!isIE && typeof MutationObserver !== 'undefined' && (
    isNative(MutationObserver) ||
    // PhantomJS and iOS 7.x
    MutationObserver.toString() === '[object MutationObserverConstructor]'
  )) {

    var counter = 1;
    var observer = new MutationObserver(flushCallbacks);
    var textNode = document.createTextNode(String(counter));
    observer.observe(textNode, {
        characterData: true
    });
    timerFunc = function () {
        counter = (counter + 1) % 2;
        textNode.data = String(counter);
    };
    isUsingMicroTask = true;

// 以上方法都不存在时,降级使用 setImmediate,它也是微任务
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
    timerFunc = function () {
        setImmediate(flushCallbacks);
    };

// 以上微任务方法都不存在,降级使用 setTimeout 宏任务
} else {
    // Fallback to setTimeout.
    timerFunc = function () {
        setTimeout(flushCallbacks, 0);
    };
}

function nextTick (cb, ctx) {
    var _resolve;
    callbacks.push(function () {
      if (cb) {
        try {
          cb.call(ctx);
        } catch (e) {
          handleError(e, ctx, 'nextTick');
        }
      } else if (_resolve) {
        _resolve(ctx);
      }
    });

    if (!pending) {
      pending = true;
      timerFunc();
    }

    // $flow-disable-line
    if (!cb && typeof Promise !== 'undefined') {
      return new Promise(function (resolve) {
        _resolve = resolve;
      })
    }
}
var timerFunc;

// 优先使用 promise 方法
if (typeof Promise !== 'undefined' && isNative(Promise)) {
    var p = Promise.resolve();
    timerFunc = function () {
        p.then(flushCallbacks);

        // ios 系统兼容
        if (isIOS) { setTimeout(noop); }
    };
    isUsingMicroTask = true;

// 降级使用 MutationObserver 方法
} else if (!isIE && typeof MutationObserver !== 'undefined' && (
    isNative(MutationObserver) ||
    // PhantomJS and iOS 7.x
    MutationObserver.toString() === '[object MutationObserverConstructor]'
  )) {

    var counter = 1;
    var observer = new MutationObserver(flushCallbacks);
    var textNode = document.createTextNode(String(counter));
    observer.observe(textNode, {
        characterData: true
    });
    timerFunc = function () {
        counter = (counter + 1) % 2;
        textNode.data = String(counter);
    };
    isUsingMicroTask = true;

// 以上方法都不存在时,降级使用 setImmediate,它也是微任务
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
    timerFunc = function () {
        setImmediate(flushCallbacks);
    };

// 以上微任务方法都不存在,降级使用 setTimeout 宏任务
} else {
    // Fallback to setTimeout.
    timerFunc = function () {
        setTimeout(flushCallbacks, 0);
    };
}

function nextTick (cb, ctx) {
    var _resolve;
    callbacks.push(function () {
      if (cb) {
        try {
          cb.call(ctx);
        } catch (e) {
          handleError(e, ctx, 'nextTick');
        }
      } else if (_resolve) {
        _resolve(ctx);
      }
    });

    if (!pending) {
      pending = true;
      timerFunc();
    }

    // $flow-disable-line
    if (!cb && typeof Promise !== 'undefined') {
      return new Promise(function (resolve) {
        _resolve = resolve;
      })
    }
}