Vue之watch监听的原理

前言

vue选项中props、data、watch、methods、computed,其中props、data、computed都通过Object.defineProperty进行数据拦截从而实现响应式。对于选项watch,知道其作用是监听对应key做相关处理,之前一系列文章都没有关注watch背后的实现逻辑,这也是本文的目的。

watch实现逻辑

首先通过vue官网的信息说明明确watch的使用方式以及注意点,如下:

watch选项的类型:{ [key: string]: string | Function | Object | Array }

键是需要观察的表达式,值是对应回调函数,可以是string类型函数名、函数、对象、数组(具体可看Vue官网Watch API)。

watch是作为组件的选项,所以在源码中处理逻辑还是initState的处理。

initState处理

initState中watch具体处理逻辑如下:

if (opts.watch && opts.watch !== nativeWatch) {
	initWatch(vm, opts.watch);
}

其中有一个注意的逻辑是nativeWatch的比较,这是因为Firefox浏览中Object.prototype存在一个watch函数。

initWatch处理

watch选项的初始化处理逻辑如下:

function initWatch (vm, watch) {
	for (var key in watch) {
		var handler = watch[key];
        if (Array.isArray(handler)) {
          for (var i = 0; i < handler.length; i++) {
            createWatcher(vm, key, handler[i]);
          }
        } else {
          createWatcher(vm, key, handler);
        }
    }
}

遍历watch中每一个key,调用createWatcher函数。其中针对回调函数是数组即多个函数处理程序的情况做处理。

createWatcher函数
// expOrFn参数即watch对应的key
function createWatcher (
      vm,
      expOrFn,
      handler,
      options
    ) {
      if (isPlainObject(handler)) {
        options = handler;
        handler = handler.handler;
      }
      if (typeof handler === 'string') {
        handler = vm[handler];
      }
      return vm.$watch(expOrFn, handler, options)
}

实际上主要逻辑就是调用$wacth实例方法,同时针对回调函数的一些情况的处理:对象、字符串函数名。

$watch处理逻辑
Vue.prototype.$watch = function (
	expOrFn,
    cb,
    options
) {
	var vm = this;
    if (isPlainObject(cb)) {
    	return createWatcher(vm, expOrFn, cb, options)
    }
    options = options || {};
    options.user = true;
    var watcher = new Watcher(vm, expOrFn, cb, options);
    if (options.immediate) {
    	try {
        	cb.call(vm, watcher.value);
       	} catch (error) {
        	handleError(error, vm, ("callback for immediate watcher \"" + (watcher.expression) + "\""));
        }
     }
     return function unwatchFn () {
     	watcher.teardown();
     }
};

实际上$watch实例方法有3点主要逻辑点:

  • Watcher对象生成
  • immediate配置:实际上就是立即调用对应的回调函数
  • unwatch逻辑处理即取消watch监听

那么watch是如何监听对应key,在key值变化时触发回调逻辑调用?

逻辑在选项watch中watcher对象生成中

涉及选项watch的Watcher构造函数逻辑如下:

var Watcher = function Watcher (
  vm,
  expOrFn,
  cb,
  options,
  isRenderWatcher
) {
  // 其他代码省略
  // parse expression for getter
  if (typeof expOrFn === 'function') {
    this.getter = expOrFn;
  } else {
    this.getter = parsePath(expOrFn);
  }
  this.value = this.lazy
    ? undefined
    : this.get();
};

首先要明确Watcher构造函数的参数含义,主要有:

  • vm:对应的vue实例
  • expOrFn:用于生成getter表达式,watcher对象的核心执行逻辑
  • cb:对应的回调函数

对于watch选项中创建watcher对象,expOrFn实际上就是选项watch中的每一项的key。从Watcher构造函数中针对exporFn生成getter方法,之后会执行getter生成对应的value值。

而针对非函数的expOrFn会调用parsePath函数,实际上就是针对选项watch中生成watcher对象的处理,而这里就是watch原理的关键。

parsePath
var bailRE = /[^\w.$]/;
function parsePath (path) {
  if (bailRE.test(path)) {
    return
  }
  // 逗号分割
  var segments = path.split('.');
  return function (obj) {
    for (var i = 0; i < segments.length; i++) {
      if (!obj) { return }
      // 关键逻辑
      obj = obj[segments[i]];
    }
    return obj
  }
}

实际上watch原理就是会获取对应key对应的值,即:

obj = obj[segments[i]];

watch选项中key必须是被数据拦截的属性(没有被拦截是无法监听变化),watch原理就是利用已被拦截属性获取来实现的,基本过程如下:

  • parsePath中会生成getter函数之后被触发
  • getter函数中会获取key表达式对应的值,而这个获取过程会触发key的getter即会触发依赖收集(实际上就会将watch下key对应的Dep对象与对应生成的watcher对象形成关联)
  • 当key对应的表达式形成新的值时,会触发notify从而视图更新,之后会调用Watcher对象的update(实际上同步或异步调用run方法即watcher对象中保存的cb回调函数,对于选项watch下key就是对应的处理函数)

通常watch可以监听data、props这些被拦截的数据很好理解,但实际上watch还可以监听computed:

computed: {
	changeMsg() {}
},
watch: {
	changeMsg() {}
}

watch原理就是利用已被拦截属性获取来实现的,这个结论是适用的,只不过computed得情况比较特殊,响应式背后的原理核心就是建立dep和watcher之间的关系,computed自身可以作为依赖,但是其不对应一个dep对象,而是computed内部的依赖与其watcher建立关系,而computed作为watch的key也是同样的,是computed内部的dep与watch对应的watcher建立联系。

总结

选项watch监听原理的关键在于:

watch下key必须是被数据拦截的属性

如何实现watch监听?正是因为watch对应key是被数据拦截的属性,在选项watch下:

  • 每一个key都会对应一个watcher对象
  • watcher对象的getter函数就是获取当前key对应的值,从而建立key对应Dep对象与当前watcher对象建立联系
  • 当key改变时就会触发视图更新,从而执行key对应的回调函数

实际上所有监听原理的实现都是Dep对象与Watcher对象建立联系,在Dep对象对应属性改变时触发视图更新,从而运行相关逻辑。

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值