Vue2.x源码学习笔记(六)——watcher异步更新

201 阅读2分钟

queueWatcher

在watcher的update方法中当没有lazy和sync配置时,watcher更新会通过queueWatcher去更新。

const queue: Array<Watcher> = []
let waiting = false
let flushing = false
function queueWatcher (watcher: Watcher) {
  const id = watcher.id
  if (has[id] == null) {
    has[id] = true
    if (!flushing) {
      queue.push(watcher)
    } else {
      let i = queue.length - 1
      while (i > index && queue[i].id > watcher.id) {
        i--
      }
      queue.splice(i + 1, 0, watcher)
    }
    if (!waiting) {
      waiting = true
      if (process.env.NODE_ENV !== 'production' && !config.async) {
        flushSchedulerQueue()
        return
      }
      nextTick(flushSchedulerQueue)
    }
  }
}

queueWatcher就是将watcher加入一个队列的过程。根据watcher的id来判断是否加入过队列。

如果当前不处于刷新队列的状态,则watcher直接push入队。如果已经处在刷新队列状态,则将id从队列后往前比较,插在对应的顺序,使queue按照id从小到大排序。然后通过nextTick包裹执行flushSchedulerQueue。

flushSchedulerQueue

function flushSchedulerQueue () {
  currentFlushTimestamp = getNow()
  flushing = true
  let watcher, id
  for (index = 0; index < queue.length; index++) {
    watcher = queue[index]
    if (watcher.before) {
      watcher.before()
    }
    id = watcher.id
    has[id] = null
    watcher.run()
  }
  const activatedQueue = activatedChildren.slice()
  const updatedQueue = queue.slice()
  resetSchedulerState()
  callActivatedHooks(activatedQueue)
  callUpdatedHooks(updatedQueue)
  if (devtools && config.devtools) {
    devtools.emit('flush')
  }
}
function resetSchedulerState () {
  index = queue.length = activatedChildren.length = 0
  has = {}
  if (process.env.NODE_ENV !== 'production') {
    circular = {}
  }
  waiting = flushing = false
}

执行flushSchedulerQueue时表示正在刷新队列,所以flushing为true。将队列中的watcher按id从小到大排序,这么做是为了:

  1. 组件的更新顺序为从父级到子级,因为父组件总是在子组件之前被创建。
  2. 一个组件的用户 watcher 在其渲染 watcher 之前被执行,因为用户 watcher 先于 渲染 watcher 创建。
  3. 如果一个组件在其父组件的 watcher 执行期间被销毁,则它的 watcher 可以被跳过。
  4. 排序以后在刷新队列期间新进来的 watcher 也会按顺序放入队列的合适位置。

紧接着循环队列,如果watcher中有before这个配置则执行before钩子,然后执行watcher的run方法。并且将执行过的watcher的id从has这个缓存map中移除。

然后通过resetSchedulerState将has重置为{},清空队列,flushing和waiting重置为false。

waiting = flushing = false,表示刷新队列结束,表示可以像 callbacks 数组中放入新的flushSchedulerQueue 函数,并且可以向浏览器的任务队列放入下一个 flushCallbacks 函数了。

最后调用actived和update生命周期函数

补充: waiting用来判断callbacks数组中是否存放了flushSchedulerQueue函。callbacks 数组中只能存在一个 flushSchedulerQueue 函数,多了没意义。