Vue.watch 源码实现

watch

vm.$watch(expOrFn,callback,[options])
功能

观察Vue实例变化的一个表达式或计算属性函数。回调函数得到的参数为新值和旧值。表达式值接收监督的键路径

参数

  • exOrFn:要监视的$data 中的属性,可以说表达式或函数
  • callback:数据变化后执行的函数
    函数:回调函数
    对象:具有handler属性(字符串或者函数),如果该属性为字符串则methods中相应的定义
  • options:可选的选项
    deep: 布尔类型,深度监听
    immediate: 布尔类型,是否立即执行一次回调函数

示例

const vm = new Vue({
   el:"#app",
   data:{
     user:{
        fisrtName: 'aaaa',
        lastName: 'bbbbb'
      }
   }
})

vm.$watch('user',function(newValue,oldValue){
   this.user.firstName = newValue.firstName + "  " + newValue.lastName
},{
 immediate: true,
 deep: true
})

三种类型的Watcher对象

  • 没有静态方法,因为$watch方法中要是用Vue的实例
  • Watcher分三种:计算属性Watcher,用户Watcher(侦听器)、渲染Watcher
    创建顺序:计算属性Watcher,用于Watcher,渲染Watcher
  • vm.$watch()
    src/core/instance/state.js
    部分代码
export function initState (vm: Component) {
  vm._watchers = []
  const opts = vm.$options
  if (opts.props) initProps(vm, opts.props) // 初始化props,并且通过defineReactive函数将值转换为set,get
  if (opts.methods) initMethods(vm, opts.methods) // 将选项中的methods注入到vue实例,
  if (opts.data) {
    initData(vm) // 
  } else {
    observe(vm._data = {}, true /* asRootData */) //转换成响应式数据
  }  
  if (opts.computed) initComputed(vm, opts.computed)  // 初始化computed
  if (opts.watch && opts.watch !== nativeWatch) {
    initWatch(vm, opts.watch)  // 初始化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)
    }
  }
}

function createWatcher (
  vm: Component,
  expOrFn: string | Function,
  handler: any,
  options?: Object
) {
  if (isPlainObject(handler)) { // hundler是回调函数,就是对应传入方法
    options = handler
    handler = handler.handler
  }
  if (typeof handler === 'string') {
    handler = vm[handler]
  }
  return vm.$watch(expOrFn, handler, options)
}

export function stateMixin (Vue: Class<Component>) {
  // flow somehow has problems with directly declared definition object
  // when using Object.defineProperty, so we have to procedurally build up
  // the object here.
  const dataDef = {}
  dataDef.get = function () { return this._data }
  const propsDef = {}
  propsDef.get = function () { return this._props }
  if (process.env.NODE_ENV !== 'production') {
    dataDef.set = function () {
      warn(
        'Avoid replacing instance root $data. ' +
        'Use nested data properties instead.',
        this
      )
    }
    propsDef.set = function () {
      warn(`$props is readonly.`, this)
    }
  }
  Object.defineProperty(Vue.prototype, '$data', dataDef)
  Object.defineProperty(Vue.prototype, '$props', propsDef)

  Vue.prototype.$set = set
  Vue.prototype.$delete = del

  Vue.prototype.$watch = function (
    expOrFn: string | Function,
    cb: any,
    options?: Object
  ): Function {
    // 获取 Vue 实例的this
    const vm: Component = this
    if (isPlainObject(cb)) {
      // 判断如果 cb 是对象执行createWatcher
      return createWatcher(vm, expOrFn, cb, options)
    }
    options = options || {}
    // 标记为用户 watcher
    options.user = true
    // 创建用户 watcher 对象
    const watcher = new Watcher(vm, expOrFn, cb, options)
    // 判断 immediate 对象
    if (options.immediate) {
      // 立即执行一次cb回调,并把当前值传入
      try {
        cb.call(vm, watcher.value)
      } catch (error) {
        handleError(error, vm, `callback for immediate watcher "${watcher.expression}"`)
      }
    }
    // 返回取消监听的方法
    return function unwatchFn () {
      watcher.teardown()
    }
  }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值