vue初始化watch

分析源码不求行行都懂,大致分析下watch得执行原理

  • 在vue源码中找到initWatch
    在这里插入图片描述
    基本得逻辑就是initWatch —> createWatcher() ----> vm.$watch() ----> new Watcher();

**1.initWatch **

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])
      }
    } else {
      createWatcher(vm, key, handler)
    }
  }
}

这个方法中其实就是将watch看成一个对象去循环处理它,最终执行createWatcher()

2.createWatcher()

function createWatcher (
  vm: Component,
  expOrFn: string | Function,
  handler: any,
  options?: Object
) {
  if (isPlainObject(handler)) {
    options = handler
    handler = handler.handler
  }
  if (typeof handler === 'string') {
    handler = vm[handler]
  }
  return vm.$watch(expOrFn, handler, options)
}

createWatcher前面部分代码就是针对watch得不同写法做了处理,比如字符串写法,函数写法,对象写法。最后调用了$watch得方法

3.$watch

Vue.prototype.$watch = function (
    expOrFn: string | Function,
    cb: any,
    options?: Object
  ): Function {
    const vm: Component = this
    if (isPlainObject(cb)) {
      return createWatcher(vm, expOrFn, cb, options)
    }
    options = options || {}
    options.user = true
    const watcher = new Watcher(vm, expOrFn, cb, options)
    if (options.immediate) {
      try {
        cb.call(vm, watcher.value)
      } catch (error) {
        handleError(error, vm, `callback for immediate watcher "${watcher.expression}"`)
      }
    }
    return function unwatchFn () {
      watcher.teardown()
    }
  }

w a t c h 这 个 方 法 中 注 意 , 它 得 核 心 是 n e w W a t c h e r ( v m , e x p O r F n , c b , o p t i o n s ) , 我 们 可 以 往 上 推 看 到 参 数 一 次 是 t h i s , 字 符 串 , 执 行 逻 辑 , u n d e f i n e d , 注 意 : 所 有 实 现 w a t c h 得 原 理 就 在 这 个 W a t c h e r 中 实 现 。 返 回 w a t c h e r . t e a r d o w n ( ) , 是 去 除 监 听 , 是 为 了 在 我 们 代 码 中 直 接 写 t h i s . watch这个方法中注意,它得核心是 new Watcher(vm, expOrFn, cb, options),我们可以往上推看到参数一次是 this,字符串,执行逻辑,undefined, 注意:所有实现watch得原理就在这个Watcher中实现。 返回 watcher.teardown(),是去除监听,是为了在我们代码中直接写this. watchnewWatcher(vm,expOrFn,cb,options)this,undefinedwatchWatcherwatcher.teardown()this.watch()得时候不用去执行监听逻辑。

4.new Watcher
1.在constructor中有这一段逻辑,会执行parsePath方法

if (typeof expOrFn === 'function') {
      this.getter = expOrFn
    } else {
      this.getter = parsePath(expOrFn)
      if (!this.getter) {
        this.getter = noop
        process.env.NODE_ENV !== 'production' && warn(
          `Failed watching path: "${expOrFn}" ` +
          'Watcher only accepts simple dot-delimited paths. ' +
          'For full control, use a function instead.',
          vm
        )
      }
    }

2.parsePath方法返回得是一个函数(这种用法为柯里化函数),返回得函数中都是要去访问对应data得 get set,方法得,这时候就会做一个依赖收集,和派发更新。

export function parsePath (path: string): any {
  if (bailRE.test(path)) {
    return
  }
  const segments = path.split('.')
  return function (obj) {
    for (let i = 0; i < segments.length; i++) {
      if (!obj) return
      obj = obj[segments[i]]
    }
    return obj
  }
}

3.接着往下看,是执行get(),pushTarget(this)会将Dep.target设置为当前得watch-watcher,然后执行getter,上面说了getter返回得是一个函数,里面会做依赖收集和派发更新,这个时候watch-watcher 就会收集进入dep,最后促发派发更新 触发watch-watcher得update方法

get () {
    pushTarget(this)
    let value
    const vm = this.vm
    try {
      value = this.getter.call(vm, vm)
    } catch (e) {
      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
  }

4。调用queueWatcher(),这里源码中可以去看下这个对应的方法中会执行flushSchedulerQueue(),最后执行watcher得run 方法

update () {
    /* istanbul ignore else */
    if (this.lazy) {
      this.dirty = true
    } else if (this.sync) {
      this.run()
    } else {
      queueWatcher(this)
    }
  }

*5.run(),最后又返回到了this.cb.call,watch得执行回调中去了,上面我们说了在new watcher得时候cb 就是,watch得执行逻辑,

run () {
    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) {
          try {
            this.cb.call(this.vm, value, oldValue)
          } catch (e) {
            handleError(e, this.vm, `callback for watcher "${this.expression}"`)
          }
        } else {
          this.cb.call(this.vm, value, oldValue)
        }
      }
    }
  }

总结:

  1. vue在初始化watch得时候都会将watch当作一个对象循环处理,并且兼容它得几种写法
  2. 它实现得核心就是 w a t c h 这 个 方 法 , 它 会 实 例 化 一 个 w a t c h − w a t c h e r , watch这个方法,它会实例化一个watch-watcher, watchwatchwatcherwatch返回是一个函数清楚监听,是为了在代码中直接写this.$watch 不会触发这些执行逻辑。
  3. watch-watcher中得处理,parsePath会先处理字符串,getter返回得是一个函数。
  4. 执行watcher.get方法,替换全局watcher,执行getter完成依赖收集。
  5. 当监听得数据变化时,也会触发它得watch-watcher,来调用update方法,其实最后执行得是它得run方法,里面会执行watcher得对应数据得执行逻辑
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值