vue 2.0 理解 watch

384 阅读1分钟

要理解watch 的实现过程,先看下watch 的几种使用方式

一个对象,键是需要观察的表达式,值是对应回调函数。值也可以是方法名,或者包含选项的对象。Vue 实例将会在实例化时调用 $watch(),遍历 watch 对象的每一个 property。

var vm = new Vue({ 
data: { 
    a: 1, b: 2, c: 3, d: 4, e: { f: { g: 5 } } 
}, 
watch: { 
a: function (val, oldVal) { 
    console.log('new: %s, old: %s', val, oldVal) 
}, 
// 方法名 
b: 'someMethod', 
// 该回调会在任何被侦听的对象的 property 改变时被调用,不论其被嵌套多深 
c: {
handler: function (val, oldVal) { /* ... */ }, 
deep: true 
}, 
// 该回调将会在侦听开始之后被立即调用 
d: { 
handler: 'someMethod',
immediate: true 
}, 
// 你可以传入回调数组,它们会被逐一调用
e: [ 
'handle1', 
function handle2 (val, oldVal) { /* ... */ },
{ handler: function handle3 (val, oldVal) { /* ... */ }, 
/* ... */ } 
], 
// watch vm.e.f's value: {g: 5} 
'e.f': function (val, oldVal) { /* ... */ } 
}
})
vm.a = 2 // => new: 2, old: 1

接下来我们顺着源码来梳理下watch 的流程

  • 在state.js 中初始化 watch
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) {
    initData(vm)
  } else {
    observe(vm._data = {}, true /* asRootData */)
  }
  if (opts.computed) initComputed(vm, opts.computed)
  if (opts.watch) initWatch(vm, opts.watch)
}
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

function createWatcher (vm: Component, key: string, handler: any) {
  let options
  if (isPlainObject(handler)) {
    options = handler
    handler = handler.handler
  }
  if (typeof handler === 'string') {
    handler = vm[handler]
  }
  vm.$watch(key, handler, options)
}

在这里我们看到,会调用watch函数,那么这个函数在哪里定义的呢?在stateMixin函数中已经将watch函数,那么这个函数在哪里定义的呢? 在 stateMixin 函数中 已经将watch挂载到 vm上了

Vue.prototype.$watch = function (
    expOrFn: string | Function,
    cb: Function,
    options?: Object
  ): Function {
    const vm: Component = this
    options = options || {}
    options.user = true
    const watcher = new Watcher(vm, expOrFn, cb, options)
    if (options.immediate) {
      cb.call(vm, watcher.value)
    }
    return function unwatchFn () {
      watcher.teardown()
    }
  }

在这里我们看到 $watch 函数返回了一个函数。 返回的函数执行可以将添加的观察取消掉。通过const watcher = new Watcher(vm, expOrFn, cb, options) 添加了观察的值,和对应的回调函数。 具体原理可以参考 之前写的文章 juejin.cn/post/699458…