浅谈Vuejs中nextTick()异步更新队列源码解析

vue官网关于此解释说明如下:

vue2.0里面的深入响应式原理的异步更新队列

官网说明如下:

只要观察到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据改变。如果同一个 watcher 被多次触发,只会一次推入到队列中。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作上非常重要。然后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际(已去重的)工作。Vue 在内部尝试对异步队列使用原生的 Promise.then 和 MutationObserver,如果执行环境不支持,会采用 setTimeout(fn, 0) 代替。

例如,当你设置 vm.someData = ‘new value' ,该组件不会立即重新渲染。当刷新队列时,组件会在事件循环队列清空时的下一个“tick”更新。多数情况我们不需要关心这个过程,但是如果你想在 DOM 状态更新后做点什么,这就可能会有些棘手。虽然 Vue.js 通常鼓励开发人员沿着“数据驱动”的方式思考,避免直接接触 DOM,但是有时我们确实要这么做。为了在数据变化之后等待 Vue 完成更新 DOM ,可以在数据变化之后立即使用 Vue.nextTick(callback) 。这样回调函数在 DOM 更新完成后就会调用。例如

源码解析

方法原型以及解析注释如下:

var nextTick = (function () {
    var callbacks = []; // 存储需要触发的回调函数
    var pending = false; // 是否正在等待的标识(false:允许触发在下次事件循环触发callbacks中的回调, true: 已经触发过,需要等到下次事件循环)
    var timerFunc; // 设置在下次事件循环触发callbacks的 触发函数

    //处理callbacks的函数
    function nextTickHandler () {
      pending = false;// 可以触发timeFunc
      var copies = callbacks.slice(0);//复制callback
      callbacks.length = 0;//清空callback
      for (var i = 0; i < copies.length; i++) {
        copies[i]();//触发callback回调函数
      }
    }

    //如果支持Promise,使用Promise实现
    if (typeof Promise !== 'undefined' && isNative(Promise)) {
      var p = Promise.resolve();
      var logError = function (err) { console.error(err); };
      timerFunc = function () {
        p.then(nextTickHandler).catch(logError);
        // ios的webview下,需要强制刷新队列,执行上面的回调函数
        if (isIOS) { setTimeout(noop); }
      };

      //如果Promise不支持,但是支持MutationObserver(h5新特性,异步,当dom变动是触发,注意是所有的dom都改变结束后触发)
    } else if (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 IE11, iOS7, Android 4.4
      var counter = 1;
      var observer = new MutationObserver(nextTickHandler);
      //创建一个textnode dom节点,并让MutationObserver 监视这个节点;而 timeFunc正是改变这个dom节点的触发函数
      var textNode = document.createTextNode(String(counter));
      observer.observe(textNode, {
        characterData: true
      });
      timerFunc = function () {
        counter = (counter + 1) % 2;
        textNode.data = String(counter);
      };
    } else {// 上面两种不支持的话,就使用setTimeout

      timerFunc = function () {
        setTimeout(nextTickHandler, 0);
      };
    }
    //nextTick接受的函数, 参数1:回调函数 参数2:回调函数的执行上下文
    return function queueNextTick (cb, ctx) {
      var _resolve;//用于接受触发 promise.then中回调的函数
      //向回调数据中pushcallback
      callbacks.push(function () {
        //如果有回调函数,执行回调函数
        if (cb) { cb.call(ctx); }
        if (_resolve) { _resolve(ctx); }//触发promise的then回调
      });
      if (!pending) {//是否执行刷新callback队列
        pending = true;
        timerFunc();
      }
      //如果没有传递回调函数,并且当前浏览器支持promise,使用promise实现
      if (!cb && typeof Promise !== 'undefined') {
        return new Promise(function (resolve) {
          _resolve = resolve;
        })
      }
    }
  })();


      

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/452.html