Vue.js 源码分析 - watcher 源码

373 阅读1分钟

「这是我参与2022首次更文挑战的第20天,活动详情查看:2022首次更文挑战

Vue.js 源码分析 - watcher 源码

接下来我们来查看 $watcher方法的实现

创建监听器

  • 路径:src\core\instance\state.js
  • 方法: initState
if (opts.watch && opts.watch !== nativeWatch) {
    initWatch(vm, opts.watch)
}

image.png 我们会首先获取页面中是否有 watch 函数,如果有的话,将该方法内的内容传入到initWatch

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)
    }
  }
}

initWatch 方法非常的简单

  • 首先遍历 watch 对象,也就是选项中传入的 watch 对象
  • 然后获取我们的handler方法,并且判断是否是数组
  • 然后根据情况调用createWatcher方法

createWatcher

参数:

  • vm: Component 当前的实例属性
  • expOrFn: string | Function -当前监听的属性
  • handler: any - 属性对应的值
  • options?: Object
  1. 代码块1
      if (isPlainObject(handler)) {
        options = handler
        handler = handler.handler
      }
    
    解析:isPlainObject:判断当前属性对应的值是否是一个原生的对象,如果是的话,会直接将handler保存到options中,并且取出对象中的handler.handler
  2. 代码块2
if (typeof handler === 'string') {
    handler = vm[handler]
  }

解析: 如果是字符串就直接将vue实例中的对象直接进行赋值

$watch

  1. 获取 Vue 实例 this,正是因为这块,所以$watch是一个实例方法,并且没有对应的静态方法,理由就是使用到了 Vue 的实例
const vm: Component = this
  1. 判断$watch的第二个参数cb是否是原始对象
if (isPlainObject(cb)) {
  // 判断如果 cb 是对象执行 createWatcher
  return createWatcher(vm, expOrFn, cb, options)
}
  1. 处理 options; 如果options 被处理过就进行赋值,否则直接赋值为空对象
 options = options || {}
  1. 标记为用户 watcher;user属性设置为 true 有什么深意呢? 如果是用户watcher的时候,需要加上 try/catch进行报错的获取

     options.user = true
    
    • watcher类
    • 路径:src\core\observer\watcher.js
    • run方法
       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)
      }
      
  2. 创建用户 watcher 对象

const watcher = new Watcher(vm, expOrFn, cb, options)
  1. 判断 immediate 如果为 true
if (options.immediate) {
      // 立即执行一次 cb 回调,并且把当前值传入
      try {
        cb.call(vm, watcher.value)
      } catch (error) {
        handleError(error, vm, `callback for immediate watcher "${watcher.expression}"`)
      }
    }