触发更新时watcher的执行

  • watcher分为三种,Computed Watcher, 用户Watcher侦听器,渲染Watcher
  • 前两种 initState 时初始化
  • 渲染watcher在 core/instance/lifecycle.js 中 mountComponent,后执行
  • 当调用 dep.notify 时,会对watcher排序,然后依次更新watcher.update
  • 对于不同类型 watcher, update处理方式不同
  • 渲染watcher会判断 当前组件watcher是否放入队列中,未放入 则找到位置插入到队列中
  • 调用 flushSchedulerQueue 方法

    • 先更新父组件后更新子组件(父组件先创建)
    • 用户watchers在渲染watcher之前运行,(用户或计算属性在initState中创建,在mountComponent之前)
    • 如果一个组件在父组件执行期间被销毁,则跳过当前组件
    • 遍历队列中的 watcher,调用 watcher.run()
    • run 方法内 调用get 方法, get方法调用 了 this.getter.call(vm, vm)
    • this.getter 就是 updateComponent 方法
  • 更新结束后重置状态
  • 调用 activated,updated两个钩子

    notify () {
    
    // copy
    const subs = this.subs.slice()
    
    if (process.env.NODE_ENV !== 'production' && !config.async) {
    
      // 按watcher的创建顺序排序
      subs.sort((a, b) => a.id - b.id)
    }
    
    // 调用 watcher的 更新
    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update()
    }
    }
    update () {
    // 三种watcher update 渲染watcher 走else
    
    if (this.lazy) {
      this.dirty = true
    } else if (this.sync) {
      this.run()
    } else {
      queueWatcher(this)
    }
    }
    export function queueWatcher (watcher: Watcher) {
    const id = watcher.id
    
    // has是个对象,防止 watcher被重复处理
    if (has[id] == null) {
    
      has[id] = true
    
      // flushing = true 表示 watcher对象正在被处理
      // 把watcher放入队列中
      if (!flushing) {
    
        queue.push(watcher)
      } else {
    
        let i = queue.length - 1
    
        // 队列未处理完,从后向前取watcher和当前组件watcher比较,确定i
        while (i > index && queue[i].id > watcher.id) {
          i--
        }
        
        // 把组件watcher插入对应位置
        queue.splice(i + 1, 0, watcher)
      }
      
      // waiting = true 表示 当前队列正在执行
      if (!waiting) {
        waiting = true
    
        if (process.env.NODE_ENV !== 'production' && !config.async) {
    
          // 遍历所有watcher,调用 watcher.run()
          flushSchedulerQueue()
    
          return
        }
    
        // 生产环境会传入nextTick,
        nextTick(flushSchedulerQueue)
      }
    }
    }
    function flushSchedulerQueue () {
    ... // 把watcher插入队列
    
    // 遍历队列内的watcher执行 run
    watcher.run()
    ...
    }
    run () {
    ...
    this.get() // this指watcher
    ...
    }
    get () {  
    pushTarget(this)
    ...
    // this.getter 为 updateComponent
    value = this.getter.call(vm,vm)
    }

mcgee0731
60 声望4 粉丝

不会做饭的程序猿不是一个好厨子