美文网首页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