Vue2.0 nextTick

目前为止,自己只在指令中使用到这个方法,贴下源码解读。

"use strict";
var vueFocus = {}
vueFocus.config = {}

vueFocus.install = function(Vue) {
    Vue.directive('focus',{
        bind: function(el,binding) {
            
        },
        update: function(el,binding) {
            if(binding.value && binding.value.flag) {
                Vue.nextTick(function () {
                    el.focus();
                    //el.value = binding.value.value //使用后会无法编辑input
                });
            }
        }
    })
}

module.exports = vueFocus;

源码部分:其实源码上的注释已经讲的比较明白了,首先返回一个闭包函数,它会将 nextTick 传入的函数 缓存进 callback 的数组中,如果没有其它函数

/**
 * Defer a task to execute it asynchronously.
 *
 */
export const nextTick = (function () {
  const callbacks = [] //缓存回调数组
  let pending = false //是否正在执行flag
  let timerFunc       //要执行的函数

  function nextTickHandler () {
    pending = false
    const copies = callbacks.slice(0) //复制函数
    callbacks.length = 0
    for (let i = 0; i < copies.length; i++) {
      copies[i]() //依次执行
    }
  }

  // 此处做了兼容处理,首先调用promise.then 来达到延迟调用
  // 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 if */
  if (typeof Promise !== 'undefined' && isNative(Promise)) {
    var p = Promise.resolve()
    var logError = err => { console.error(err) }
    timerFunc = () => {
      p.then(nextTickHandler).catch(logError)
      // 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)
    }
  } else if (typeof MutationObserver !== 'undefined' && ( //其次使用 html5 新增的 MutationObserver 来监听 DOM 操作结束
    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)
    var textNode = document.createTextNode(String(counter))
    observer.observe(textNode, {
      characterData: true
    })
    timerFunc = () => {
      counter = (counter + 1) % 2
      textNode.data = String(counter)
    }
  } else { //利用setTimeout,setTimeout(func, 0)会将func函数延迟到下一次函数调用栈的开始,也就是当前函数执行完毕后再执行该函数,因此完成了延迟功能。
    // fallback to setTimeout
    /* istanbul ignore next */
    timerFunc = () => {
      setTimeout(nextTickHandler, 0)
    }
  }

  return function queueNextTick (cb?: Function, ctx?: Object) {
    let _resolve
    callbacks.push(() => {
      if (cb) {
        try {
          cb.call(ctx)
        } catch (e) {
          handleError(e, ctx, 'nextTick')
        }
      } else if (_resolve) {
        _resolve(ctx)
      }
    })
    if (!pending) {  //如果没有函数在执行,则进行下一步,执行传入的函数
      pending = true
      timerFunc()
    }
    if (!cb && typeof Promise !== 'undefined') { //如果不传函数,则可以使用 $nextTick().then()的这种方法
      return new Promise((resolve, reject) => {
        _resolve = resolve
      })
    }
  }
})()