Vue 2.x 源码解读系列《watch 监听属性》

前言

watch 属性在 vue 中很常用,我们可以用它来监听某些属性,进行做一些逻辑处理。

initWatch(vm, opts.watch) 入口方法在 initState() 方法中开始

export function initState(vm: Component) {
  vm._watchers = []
  const opts = vm.$options
  if (opts.props) initProps(vm, opts.props)
  if (opts.methods) initMethods(vm, opts.methods)
  if (opts.data) {
    // 初始化 data
    initData(vm)
  } else {
    observe(vm._data = {}, true /* asRootData */)
  }
  if (opts.computed) initComputed(vm, opts.computed)
  // 如果组件中存在 watch ,并且不是原生的 watch,那么就初始化 watch
  // 这里的 watch 为我们在组件中定义的用来监听属性变化的那个 watch
  if (opts.watch && opts.watch !== nativeWatch) {
    // 传入实例以及组件中添加的 watch 对象
    initWatch(vm, opts.watch)
  }
}

initWatch() 方法

function initWatch(vm: Component, watch: Object) {
  // 遍历 watch 中的 key 值,这个值即为我们在组件中通过 watch 监听的字段名
  for (const key in watch) {
    const handler = watch[key]
    // 如果 handler 是一个数组(一个属性,对应多个处理函数),那么会给第一个处理函数都创建一个 watcher 实例
    if (Array.isArray(handler)) {
      for (let i = 0; i < handler.length; i++) {
        createWatcher(vm, key, handler[i])
      }
    } else {
      createWatcher(vm, key, handler)
    }
  }
}

createWatcher()

// 创建一个 watcher
function createWatcher(
  vm: Component,
  expOrFn: string | Function,
  handler: any,
  options?: Object
) {
  // 如果 handler 是一个纯对象
  if (isPlainObject(handler)) {
    // 缓存 handler
    options = handler
    // 取出 handler 对象中的处理函数
    handler = handler.handler
  }
  // 如果 hanlder 为字符串,则 handler 的值取 vm 对象上的同名属性值
  if (typeof handler === 'string') {
    handler = vm[handler]
  }
  // 调用 vue 实例上的 $watch() 方法,expOrFn 为监听的属性名,handler 为字符串,options 为额外的配置项
  return vm.$watch(expOrFn, handler, options)
}

阅读 initWatch() 方法和 createWatcher() 方法后可以知道 watch 的写法有多种,监听的属性值可以是一个数组,可以是一个函数,可以是一个对象,还可以是一个字符串。

  • 如果是一个数组,则把数组的每一项作为参数传给 createWatcher() 方法
  • 如果是一个函数,则把它作为监听到属性变化后的回调函数
  • 如果是一个对像,则把这个对象里的 handler 作为监听属性变化后的回调函数,对象中的其它属性,则作为配置项(比如:immediate)
  • 如果是一个字符串,则相当于取 vm 实例上的同名函数

vm.$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
    // vm 为 vue 实例,expOrFn 为监听的属性名,cb 为监听的属性名所对应的监听的回调函数
    const watcher = new Watcher(vm, expOrFn, cb, options)
    // 如果有设置 immediate 属性,则立即执行一遍函数
    if (options.immediate) {
      try {
        // watcher.value 的值
        cb.call(vm, watcher.value)
      } catch (error) {
        handleError(error, vm, `callback for immediate watcher "${watcher.expression}"`)
      }
    }
    return function unwatchFn() {
      watcher.teardown()
    }
  }
声明

1.原创文章,不经本站同意,不得以任何形式转载,如有不便,请多多包涵!

2.本文永久链接:http://yunkus.com/post/5e46c85b57e4b51b

3.如果觉得本文对你有帮助,或者解决了你的问题,不妨扫一扫右边的二维码打赏支持,你的一分一毫,可能会让世界变得更美好。

微信
扫一扫,赏我
支付宝
扫一扫,赏我