关于vue源码flushSchedulerQueue函数queue.sort使用疑惑

先看源码:

function flushSchedulerQueue () {
  currentFlushTimestamp = getNow()
  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)

  // do not cache length because more watchers might be pushed
  // as we run existing watchers
  for (index = 0; index < queue.length; index++) {
    watcher = queue[index]
    if (watcher.before) {
      watcher.before()
    }
    id = watcher.id
    has[id] = null
    watcher.run()
    // in dev build, check and stop circular updates.
    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
      }
    }
  }

这个方法主要是用来执行微任务后回调相关逻辑,queue里缓存了要等待更新的watcher列表,并且将queue按照从小到大进行排序。

 queue.sort((a, b) => a.id - b.id)

问题来了,为什么要对queue进行排序?

搜索出来的结果基本上是从英文里翻译过来:

  1. 组件的更新由父到子;因为父组件的创建过程是先于子的,所以 watcher 的创建也是先父后子,执行顺序也应该保持先父后子。
  2. 用户的自定义 watcher 要优先于渲染 watcher 执行;因为用户自定义 watcher 是在渲染 watcher 之前创建的。
  3. 如果一个组件在父组件的 watcher 执行期间被销毁,那么它对应的 watcher 执行都可以被跳过,所以父组件的 watcher 应该先执行。

看到这样的回答,有点似懂非懂的感觉,果断用实例来验证下:


    
      
    
    


断点queue.sort


此时,queue里保存着子组件渲染watcher(称 s)和主渲染watcher(称 p),排序之后的顺序为:[p, s]

其中s这个watcher运行之后,在run函数里边就断掉了!

watcher源文件摘取代码:

run () {
  // 此时的active已经为false
    if (this.active) {
      const value = this.get()
      if (
        value !== this.value ||
        // Deep watchers and watchers on Object/Arrays should fire even
        // when the value is the same, because the value may
        // have mutated.
        isObject(value) ||
        this.deep
      ) {
        // set new value
        const oldValue = this.value
        this.value = value
        if (this.user) {
          const info = `callback for watcher "${this.expression}"`
          invokeWithErrorHandling(this.cb, this.vm, [value, oldValue], this.vm, info)
        } else {
          this.cb.call(this.vm, value, oldValue)
        }
      }
    }
  }

这个active其实是在主渲染watcher执行中被change掉了,这样可以避免子组件的渲染watcher少执行很多代码啊,节省了很多的性能开销!

以上是个人的一点见解,欢迎留言讨论,喜欢请记得关注哦!

展开阅读全文

页面更新:2024-03-01

标签:先父   似懂非懂   英文   源文件   断点   开销   函数   组件   源码   顺序   疑惑   代码   用户

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号

Top