Skip to content
On this page

派发更新

上一文中可以知道,在渲染函数执行的过程中,会访问到data数据,从而触发getter,进行依赖收集,在Dep实例的订阅者列表中添加渲染Watcher,在渲染Watcher的依赖池中会添加Dep实例

那么,当要修改data中的某个值的时候,就会触发对应的setter

js
// observer/index.js
set: function reactiveSetter (newVal) {
  const value = getter ? getter.call(obj) : val;
  if (newVal === value || newVal !== newVal && value !== value) {
    // 前后值相同,中断
    return;
  }
  if (process.env.NODE_ENV !== 'production' && customSetter) {
    // 这里就是我们平时可能修改props时看到的 Avoid mutating props...
    // 在开发环境下会,进行props响应式设立时会传入对应的customSetter
    customSetter()
  }
  if (getter && !setter) return; // 这一行是对于一些没有setter的属性:computed
  if (setter) {
    setter.call(obj, newVal)
  } else {
    val = newVal
  }
  childOb = !shallow && observer(newVal) 
  // 如果重新赋值的也是个对象类型,就将他也变为响应式数据
  dep.notify() // 这就是通过这个值对应的Dep实例来派发更新
}

notify

js
// observer/dep.js
notify () {
	const subs = this.subs.slice() // 订阅者watcher列表
  subs.sort((a, b) => a.id - b.id) 
  // 对watcher排序
  for (let i = 0, l = subs.length; i < l; i++) {
    subs[i].update()
    // 调用watcher的update方法
  }
}

update

js
// observer/watcher.js
update () {
  if (this.lazy) {
    this.dirty = true; // 延迟计算的属性
  } else if (this.sync) {
    this.run() // sync修饰符
  } else {
    queueWatcher(this) // 将订阅这个值变化的watcher推入更新队列
  }
}

queueWatcher

js
// ovserver/scheduler.js
function queueWatcher (watcher) {
  const id = watcher.id
  if (has[id] == null) {
    has[id] = true
    if (!flusing) { // 更新开始的标识位
      // 还没开始更新
      queue.push(watcher) // 队列推入
    } else {
      // 更新已经开始
      let i = queue.length - 1 // 当前队列最后一个,排序之后id最大的一个
      while (i > index && queue[i].id > watcher.id) {
        i--
      }
      queue.splice(i + 1, 0, watcher) // 找到刚好放下这个id的watcher的位置,插入
    }
    if (!waiting) { // 一开始是设置为false
      waiting = true
      nextTick(flushSchedulerQueue) // 下一帧开始flush更新,这是个异步的过程
    }
  }
}

flushSchedulerQueue

js
// observer/scheduler.js
function flushSchedulerQueue () {
  currentFlushTimestamp = getNow()
  flushing = true // 开始更新的标识
  let watcher, id
  queue.sort((a, b) => a.id - b.id) 
  // 将队列中的watcher排序,父组件创建在先,排序后也在前面
  // 组件内的watch选项或者$watch,他们创建的 user-watcher 也在 渲染watcher 之前
  // 若是父组件运行过程中被销毁,就会忽略子组件的watcher更新
  
  for (index = 0; index < queue.length; index++) {
   	watcher = queue[index]
    if (watcher.before) {
      watcher.before()
    }
    id = watcher.id
    has[id] = null // 执行之后剔除
    watcher.run() // 这里就是每个watcher 的更新
    if (process.env.NODE_ENV !== 'production' && has[id] != null) {
      // 这里是由于可能在watcher的run方法中,重新又修改了某个之前本已经被剔除出去的值
      // 导致这个值对应的watcher又触发了
      circular[id] = (circular[id] || 0) + 1
      if (circular[id] > MAX_UPDATE_COUNT) {
        // 更新栈过大,可能是循环更新
      }
    }
    const active  const activatedQueue = activatedChildren.slice()
    const updatedQueue = queue.slice()
    resetSchedulerState() // 重置一些状态
    callActivatedHooks(activatedQueue) // keep-alive相关
    callUpdatedHooks(updatedQueue) // updated钩子 仅对渲染watcher
  }
  
}

Watcher里的run

js
// observer/watcher.js
run () {
  if (this.active) { // 创建的时候设为true
    const value = this.get()
    // 渲染watcher来说,this.cb为noop,调用this.get() 就是调用 updateComponent,重新patch
    if (value !== this.value || isObject(value) || this.deep)  {
      // 前后值不一样、对象类型数据、深度遍历watcher
      const oldValue = this.value
      this.value = value
      this.cb.call(this.vm, value, oldValue) // watchx
    }
  }
}