vue2 nextTick在源码中应用

47 阅读1分钟

nectTick管理callbacks队列,把执行callbacks队列的函数放在异步任务中。

const callbacks = []
let pending = false

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

function getTimerFunc() {
    let timerFunc
    if (typeof Promise !== 'undefined' && isNative(Promise)) {
        const p = Promise.resolve()
        timerFunc = () => {
            p.then(flushCallbacks)
        }
    } else {
    // Fallback to setTimeout.
        timerFunc = () => {
            setTimeout(flushCallbacks, 0)
        }
    }
    return timerFunc
}

function nextTick (cb?: Function) {
  let timerFunc  
  callbacks.push(() => {
    if (cb) {
        cb.call(ctx)
    }
  })
  if (!pending) {
    pending = true
    timerFunc = getTimerFunc()
    timerFunc()
  }
}

执行watcher队列函数作为nextTick callbacks中的一个callback。

let flushing = false
let waiting = false
function queueWatcher (watcher: Watcher) {
  const 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.
      let 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
      nextTick(flushSchedulerQueue)
    }
  }
}
class Watcher {
    /**
    * Subscriber interface.
    * Will be called when a dependency changes.
    */
    update () {
        queueWatcher(this)
    }
}

function flushSchedulerQueue () {
  flushing = true
  let watcher, id

  // Sort queue before flush.
  // This ensures that:
  // 1. Components are updated from parent to child. (because parent is always
  //    created before the child)
  // 2. A component's user watchers are run before its render watcher (because
  //    user watchers are created before the render watcher)
  // 3. If a component is destroyed during a parent component's watcher run,
  //    its watchers can be skipped.
  queue.sort((a, b) => a.id - b.id)

  for (index = 0; index < queue.length; index++) {
    watcher = queue[index]
    if (watcher.before) {
      watcher.before()
    }
    id = watcher.id
    has[id] = null
    watcher.run()
  }
}

通过nextTick对外提供的接口,向callbacks队列中添加callback。

Vue.nextTick(function(){
    console.log(vm.$el) 
})

nextTick方法之所以能够获取异步更新DOM后的DOM元素,在data的值改变后更新watcher队列的函数flushSchedulerQueue先放到nextTick的callbacks队列,nextTick对外的api再向callbacks队列添加cb,[flushSchedulerQueue, cb, cb, ....]。