Vue—nextTick的实现原理

使用nextTick

全局使用:

// DOM 还没有更新
Vue.nextTick(function () {
  // DOM 更新了
})

// 作为一个 Promise 使用
Vue.nextTick().then(function () {
    // DOM 更新了
})

实例方法:

new Vue({
  // ...
  methods: {
    // ...
    example: function () {
      // 修改数据
      this.message = 'changed'
      // DOM 还没有更新
      this.$nextTick(function () {
        // DOM 现在更新了
        // `this` 绑定到当前实例
        this.doSomethingElse()
      })
    }
  }
})
//全局方法
Vue.nextTick = nextTick;
//实例方法
Vue.prototype.$nextTick = function (fn) {
    return nextTick(fn, this)
  };

nextTick方法:

Vue在更新DOM时是异步执行,只要监听到数据变化,Vue
将开启一个队列,缓冲在同一事件循环中发生的所有数据变更。

在下一个事件循环tick中,Vue刷新队列并执行实际工作。(Vue内部对异步队列尝试使用原生Promise.then , MutationObserver, setImmediate。如果不支持,就采用setTimeout

为了在数据变化之后等待Vue完成更新DOM, 可以在数据变化之后立即使用Vue.nextTick(callback),这样回调函数将在DOM更新完成后被调用

返回一个Promise对象,可以只用async/await语法

var callbacks = [];
var pending = false;

//刷新回调队列,Vue刷新队列并执行实际工作
function flushCallbacks () {
  pending = false;
  var copies = callbacks.slice(0);
  callbacks.length = 0;
  for (var i = 0; i < copies.length; i++) {
    copies[i](); //执行回调方法
  }
}

//是否启用宏任务
var useMacroTask = false;
//在events的创建和更新阶段调用
function withMacroTask (fn) {
  return fn._withTask || (fn._withTask = function () {
    useMacroTask = true;
    var res = fn.apply(null, arguments);
    useMacroTask = false;
    return res
  })
}

//将开启一个队列,缓冲在同一事件循环中发生的所有数据变更。
//微任务队列和宏任务队列,各自缓存一份
var microTimerFunc;
var macroTimerFunc;

//判断是否支持setImmediate, 指定宏任务
if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
  macroTimerFunc = function () {
    setImmediate(flushCallbacks);
  };
} else if (typeof MessageChannel !== 'undefined' && (
  isNative(MessageChannel) ||
  // PhantomJS
  MessageChannel.toString() === '[object MessageChannelConstructor]'
)) {
  var channel = new MessageChannel();
  var port = channel.port2;
  channel.port1.onmessage = flushCallbacks;
  macroTimerFunc = function () {
    port.postMessage(1);
  };
} else {
  /* istanbul ignore next */
  macroTimerFunc = function () {
    setTimeout(flushCallbacks, 0);
  };
}

//判断是否支持Promise, 指定微任务
if (typeof Promise !== 'undefined' && isNative(Promise)) {
  var p = Promise.resolve();
  microTimerFunc = function () {
    p.then(flushCallbacks);
    //通过添加一个空定时器,强制刷新微任务队列
    if (isIOS) { setTimeout(noop); }
  };
} else {
  // fallback to macro
  microTimerFunc = macroTimerFunc;
}
//nextTick方法
function nextTick (cb, ctx) {
  var _resolve;
  //回调队列中插入cb
  callbacks.push(function () {
    if (cb) {
      try {
        cb.call(ctx); //传入指定的上下文
      } catch (e) {
        handleError(e, ctx, 'nextTick');
      }
    } else if (_resolve) {
      _resolve(ctx);
    }
  });
  
  if (!pending) {
    pending = true;
    //根据useMacroTask的值判断执行微任务队列还是宏任务队列
    if (useMacroTask) {
      macroTimerFunc();
    } else {
      microTimerFunc();
    }
  }
  // $flow-disable-line
  //如果没有指定回调,并且支持Promise,那么最终返回一个Promise对象
  if (!cb && typeof Promise !== 'undefined') {
    return new Promise(function (resolve) {
      _resolve = resolve;
    })
  }
}
发布了66 篇原创文章 · 获赞 13 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/haoyanyu_/article/details/102455896