美文网首页vue
vue解析5-nextTick

vue解析5-nextTick

作者: 百里哈哈 | 来源:发表于2020-04-12 19:34 被阅读0次

nextTick

在下次 DOM 更新循环结束之后执行延迟回调。有官方描述可知nextTick会等到DOM更新完成之后在进行调用, 它事先会将nexTick中要执行的函数放入一个异步队列。
其代码结构如下

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;
    })
  }
}

1.将cb放入callbacks中

  1. 如果是非pending则重新创建一个timerFunc任务

timerFunc用过创建一个任务

timerFunc用过创建一个任务队列,采用兼容性的方式进行实现。
1.a.如果浏览器支持Promise则创建一个p = Promise.resolve();
b.如果MutationObserver则使用MutationObserver
c.如果支持setImmediate则使用
d.否则使用setTimeout
2.调用回调函数flushCallbacks
其代码如下

var timerFunc;

// The nextTick behavior leverages the microtask queue, which can be accessed
// via either native Promise.then or MutationObserver.
// MutationObserver has wider support, however it is seriously bugged in
// UIWebView in iOS >= 9.3.3 when triggered in touch event handlers. It
// completely stops working after triggering a few times... so, if native
// Promise is available, we will use it:
/* istanbul ignore next, $flow-disable-line */
if (typeof Promise !== 'undefined' && isNative(Promise)) {
  var p = Promise.resolve();
  timerFunc = function () {
    p.then(flushCallbacks);
    // In problematic UIWebViews, Promise.then doesn't completely break, but
    // it can get stuck in a weird state where callbacks are pushed into the
    // microtask queue but the queue isn't being flushed, until the browser
    // needs to do some other work, e.g. handle a timer. Therefore we can
    // "force" the microtask queue to be flushed by adding an empty timer.
    if (isIOS) { setTimeout(noop); }
  };
  isUsingMicroTask = true;
} else if (!isIE && typeof MutationObserver !== 'undefined' && (
  isNative(MutationObserver) ||
  // PhantomJS and iOS 7.x
  MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
  // Use MutationObserver where native Promise is not available,
  // e.g. PhantomJS, iOS7, Android 4.4
  // (#6466 MutationObserver is unreliable in IE11)
  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;
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
  // Fallback to setImmediate.
  // Technically it leverages the (macro) task queue,
  // but it is still a better choice than setTimeout.
  timerFunc = function () {
    setImmediate(flushCallbacks);
  };
} else {
  // Fallback to setTimeout.
  timerFunc = function () {
    setTimeout(flushCallbacks, 0);
  };
}

flushCallbacks

通过flushCallbacks来执行callbacks中的回调函数

function flushCallbacks () {
  pending = false;
  var copies = callbacks.slice(0);
  callbacks.length = 0;
  for (var i = 0; i < copies.length; i++) {
    copies[i]();
  }
}

我们知道在Vue中如果更改其data的话会触发视图的更新。 而数据变化触发视图更新并不是及时进行的,而是将需要更新的watcher对象放入到更新队列中,这是有nextTick来完成的。

function queueWatcher (watcher) {
  var id = watcher.id;
  if (has[id] == null) {
    has[id] = true;
    if (!flushing) {
      queue.push(watcher);
    } else {
      // if already flushing, splice the watcher based on its id
      // if already past its id, it will be run next immediately.
      var i = queue.length - 1;
      while (i > index && queue[i].id > watcher.id) {
        i--;
      }
      queue.splice(i + 1, 0, watcher);
    }
    // queue the flush
    if (!waiting) {
      waiting = true;

      if (process.env.NODE_ENV !== 'production' && !config.async) {
        flushSchedulerQueue();
        return
      }
    // flushSchedulerQueue用于watcher批量更新的callback函数
      nextTick(flushSchedulerQueue);
    }
  }
}

示例

temp.vue

   <span>name</span>
    <button @click="testFunc">to test</button>
  ...
methods: {
    change() {
      this.name = 'ali' + Date.now();
    },
    testFunc() {
      this.change();
      this.$nextTick(function testCb() {
        console.log('the nextTick')
      })
    }
  }

看这个示例,当我们调用this.change方法的时候会将flushSchedulerQueue推入callbacks中, 然后调用this.$nextTick(function testCb() { console.log('the nextTick') }) 将对于的testCb方法继续推入callbacks队列。 执行时会先flushSchedulerQueue,后执行testCb。 而我们知道知道执行flushSchedulerQueue会完成dom对应的更新

相关文章

网友评论

    本文标题:vue解析5-nextTick

    本文链接:https://www.haomeiwen.com/subject/cfvbmhtx.html