【Vue】watch中的deep:true源码实现

当用户指定了watch中的deep属性为true时,如果当时监控的属性是数组类型,会对对象中的每一项进行求值,此时会将当前watcher存入到对应属性的依赖中,这样数组中对象发生变化时也会通知数据更新。内部原理就是递归,耗费性能 。

整体流程: 

initWatch 初期化user watcher(1),user watcher在defineReactive的get中订阅属性的变化(2),在defineReactive的set时触发notify(2),notify调用每个订阅了改属性变化的watcher的update(3),监听 watcher 进入update的queueWatcher,在queueWatcher的nextTick中调用flushSchedulerQueue(4),flushSchedulerQueue中调用监听watcher的run(5),在watcher.run中调用this.get,在this.get中判断this.deep是否为true,为true则执行traverse,在traverse中会因为对属性的取值触发2的get方法,并且traverse递归调用,使当前watch监听到对象内部的每一个属性(6),进而调用用户在watch属性上定义的方法。 

1、initWatch 初期化user watcher(src\core\instance\state.js) 

function initWatch (vm: Component, watch: Object) {
  for (const key in watch) {
    const handler = watch[key]
    if (Array.isArray(handler)) {
      for (let i = 0; i < handler.length; i++) {
        createWatcher(vm, key, handler[i])//每一项创建一个watcher
      }
    } else {
      createWatcher(vm, key, handler)
    }
  }
}

2、user watcher在defineReactive的get中订阅属性的变化,在defineReactive的set时触发notify

export function defineReactive (
  obj: Object,
  key: string,
  val: any,
  customSetter?: ?Function,
  shallow?: boolean
) {
  ...
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter () {
      const value = getter ? getter.call(obj) : val
      if (Dep.target) {
        dep.depend()
        if (childOb) {
          childOb.dep.depend()
          if (Array.isArray(value)) {
            dependArray(value)
          }
        }
      }
      return value
    },
    set: function reactiveSetter (newVal) {
      ...
      dep.notify()
    }
  })
}

3、notify调用每个订阅了改属性变化的watcher的update

notify () {
    ...
    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update()
    }
  }

4、queueWatcher(src\core\observer\scheduler.js): 

export function queueWatcher (watcher: Watcher) {
   ...
   nextTick(flushSchedulerQueue)
}

5、调用监听watcher的run

function flushSchedulerQueue () {
  ...
  for (index = 0; index < queue.length; index++) {
  ...
    watcher.run()
  ...
   }
  ...    
}

6、在this.get中判断this.deep是否为true,为true则执行traverse

get() {
    pushTarget(this)
    let value
    const vm = this.vm
    try {
      value = this.getter.call(vm, vm)
    } catch (e: any) {
      if (this.user) {
        handleError(e, vm, `getter for watcher "${this.expression}"`)
      } else {
        throw e
      }
    } finally {
      // "touch" every property so they are all tracked as
      // dependencies for deep watching
      if (this.deep) {
        traverse(value)
      }
      popTarget()
      this.cleanupDeps()
    }
    return value
  }

7、traverse

posted @ 2020-11-25 11:18  vickylinj  阅读(1356)  评论(0编辑  收藏  举报