探索vue初始化选项中的watch

通过查阅vue的官方API文档,得知watch选项的定义形式有一下几种


 watch: {
    // 1. 直接定义处理函数
    "watchedData1": function(){}
    // 2. 在methods中定义处理函数,在watch中通过函数名访问
    "watchedData2": "functionName"
    // 3. 以 watch options 的形式定义
    "watchedData3": {
       handler: function(){},
       immediate: true,
       deep: true
    }
    // 4. 处理函数为多个函数,可以通过数组的形式定义
    "watchedData4": [
      function handler1 () {},
      "handler2",
      {
         handler: function handler3(){},
         immediate: true,
         deep: true
      },
      {
         handler: "handler4",
         immediate: true,
         deep: true
      }
    ]
}

那么在vue源码中是怎么处理这些形式的数据的呢?

vue中的初始化选项包括props、data、methods、computed和watch都是在_init()函数中的initState(vm)函数中处理的,_init()函数中所做的事情有:

在core/instance/state.js文件中initWatch方法,是处理watch选项的入口: 

function initWatch (vm: Component, watch: Object) {
  // 遍历watch属性中的key
  for (const key in watch) {
    // 监控key属性的处理方法,注意此处的处理方法可以是数组
    const handler = watch[key]
    // 
    /**
     * 如果是数组,遍历每一个处理函数
     * 例如:"watchedData": [
     *      function handler1 () {},
     *      "handler2",
     *      {
     *        handler: function handler3(){},
     *        immediate: true,
     *        deep: true
     *      },
     *      {
     *        handler: "handler4",
     *        immediate: true,
     *        deep: true
     *      }
     *    ]
     */
    if (Array.isArray(handler)) {
      for (let i = 0; i < handler.length; i++) {
        createWatcher(vm, key, handler[i])
      }
    } else {
      createWatcher(vm, key, handler)
    }
  }
}

遍历每一个被监视的数据,为这些数据创建一个或多个Watcher,其中用到的核心函数就是createWatcher:

function createWatcher (
  vm: Component,
  expOrFn: string | Function,
  handler: any,
  options?: Object
) {
  /**
   * 如果handler是对象类型
   * 例如:
   * "watchedData": {
   *    handler: "functionName",
   *    immediate: true,
   *    deep: true
   * }
   * 将watchedData对应的值赋给options,取出其handler值,作为处理函数
   */
  if (isPlainObject(handler)) {
    options = handler
    handler = handler.handler
  }
  /**
   * 如果handler是字符串,则从methods中查找对应的方法(因为在初始化过程中,methods的方法被直接挂载到了实例上,所以可以通过vm[handler]获得)
   * 例如:"watchedData": "functionName"
   */
  if (typeof handler === 'string') {
    handler = vm[handler]
  }
  /**
   * 调用vm.$watch时的handler,可能是字符串,可能是函数,也可能是数组
   */
  return vm.$watch(expOrFn, handler, options)
}

在createWatcher函数中可以看到,最终还是调用了vm.$watch这个函数创建Watcher的,这个函数在哪里呢?$watch函数是在执行_init()初始化vue实例之前,通过stateMixin挂载到Vue原型上的

function Vue (options) {
  if (process.env.NODE_ENV !== 'production' &&
    !(this instanceof Vue)
  ) {
    warn('Vue is a constructor and should be called with the `new` keyword')
  }
  this._init(options)
}

initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)

export default Vue

$watch函数具体是怎么处理的呢

/**
   * 由ceateWatcher调用时,cb可能是字符串,可能是函数,也可能是数组
   * 当由Vue或vue 实例 vm调用时,cb可能是字符串,可能是函数,可能是数组,也可能是对象
   */
  Vue.prototype.$watch = function (
    expOrFn: string | Function,
    cb: any,
    options?: Object
  ): Function {
    const vm: Component = this
    // 处理cb是对象的情况
    if (isPlainObject(cb)) {
      return createWatcher(vm, expOrFn, cb, options)
    }
    options = options || {}
    // options.user用来表明创建的Watcher是用户创建的(自定义Watcher)还是非用户自定义Watcher(渲染Watcher或computed Watcher)
    options.user = true
    const watcher = new Watcher(vm, expOrFn, cb, options)
    // 如果immediate选项为true,立即执行回调函数
    if (options.immediate) {
      // 因为用户自定义的回调函数存在一定的不确定性,在执行过程中可能会报错,所以用 try catch 进行错误捕获
      try {
        cb.call(vm, watcher.value)
      } catch (error) {
        handleError(error, vm, `callback for immediate watcher "${watcher.expression}"`)
      }
    }
    // 返回一个函数,该函数用于卸载watcher对象,解除数据监控
    return function unwatchFn () {
      watcher.teardown()
    }
  }

由上可知,initWatch函数在处理watch选项时,是在initWatch函数中处理是否是数组的情况,在createWatcher中处理是否是对象的情况,最后在$watch中创建真正的Watcher

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值