前言:

       因为不采用异步更新,在每次更新数据都会对当前组件进行重新渲染。所以为了性能考虑,vue 会在本轮数据更新后,再去异步更新视图。

       vue是组件级更新,当前组件里的数据变了,它就会去更新这个组件。当数据更改一次组件就要重新渲染一次,性能不高,为了防止数据一更新就更新组件,所以做了个异步更新渲染。(核心的方法就是nextTick)

原理:

当数据变化后会调用notify方法,将watcher遍历,调用update方法通知watcher进行更新,这时候watcher并不会立即去执行,在update中会调用queueWatcher方法将watcher放到了一个队列里,在queueWatcher会根据watcher的进行去重,多个属性依赖一个watcher,如果队列中没有该watcher就会将该watcher添加到队列中,然后通过nextTick异步执行flushSchedulerQueue方法刷新watcher队列。flushSchedulerQueue中开始会触发一个before的方法,其实就是beforeUpdate,然后watcher.run() 才开始真正执行watcher,执行完页面就渲染完成啦,更新完成后会调用updated钩子。

源码:

1.notify

  notify () { // 通知存储的依赖更新
    const subs = this.subs.slice()
    if (process.env.NODE_ENV !== 'production' && !config.async) {
      subs.sort((a, b) => a.id - b.id)
    }
    // 循环watcher,发布订阅模式
    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update() // 调用watcher中的update方法
    }
  }

dep中存储的就是watcher,每个属性一个dep

2.update

  update () {
    if (this.lazy) { // 计算属性
      this.dirty = true
    } else if (this.sync) { // 同步watcher
      this.run()
    } else {
      queueWatcher(this) // 将watcher放入队列,this就是当前的watcher
    }
  }

3.queueWatcher

export function queueWatcher (watcher: Watcher) {
  const id = watcher.id // 过滤watcher,每个watcher有一个id,多个属性依赖同一个watcher
  if (has[id] == null) { // 如果没有就会添加进去
    has[id] = true
    if (!flushing) {
      queue.push(watcher) // 并且将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() // 会做一个清空queue的操作
        return
      }
      nextTick(flushSchedulerQueue) // 在下一个tick中刷新watcher队列
    }
  }
}

4.flushSchedulerQueue

function flushSchedulerQueue () {
  currentFlushTimestamp = getNow()
  flushing = true
  let watcher, id
  queue.sort((a, b) => a.id - b.id)
  for (index = 0; index < queue.length; index++) {
    watcher = queue[index]
    if (watcher.before) {
      watcher.before() // 触发一个before方法
    }
    id = watcher.id
    has[id] = null
    watcher.run() // 开始执行watcher,执行完页面就渲染完成啦
    if (process.env.NODE_ENV !== 'production' && has[id] != null) {
      circular[id] = (circular[id] || 0) + 1
      if (circular[id] > MAX_UPDATE_COUNT) {
        warn(
          'You may have an infinite update loop ' + (
            watcher.user
              ? `in watcher with expression "${watcher.expression}"`
              : `in a component render function.`
          ),
          watcher.vm
        )
        break
      }
    }
  }
  const activatedQueue = activatedChildren.slice()
  const updatedQueue = queue.slice()
  resetSchedulerState()
  callActivatedHooks(activatedQueue)
  callUpdatedHooks(updatedQueue) // 更新完成后会调用updated钩子
  if (devtools && config.devtools) {
    devtools.emit('flush')
  }
}

 

Logo

前往低代码交流专区

更多推荐