vue选项中props、data、watch、methods、computed,其中props、data、computed都通过Object.defineProperty进行数据拦截从而实现响应式。对于选项watch,知道其作用是监听对应key做相关处理,之前一系列文章都没有关注watch背后的实现逻辑,这也是本文的目的。
首先通过vue官网的信息说明明确watch的使用方式以及注意点,如下:
watch选项的类型:{ [key: string]: string | Function | Object | Array }
键是需要观察的表达式,值是对应回调函数,可以是string类型函数名、函数、对象、数组(具体可看Vue官网Watch API)。
watch是作为组件的选项,所以在源码中处理逻辑还是initState的处理。
initState中watch具体处理逻辑如下:
if (opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch);
}
其中有一个注意的逻辑是nativeWatch的比较,这是因为Firefox浏览中Object.prototype存在一个watch函数。
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函数。其中针对回调函数是数组即多个函数处理程序的情况做处理。
// 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实例方法,同时针对回调函数的一些情况的处理:对象、字符串函数名。
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点主要逻辑点:
那么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构造函数的参数含义,主要有:
对于watch选项中创建watcher对象,expOrFn实际上就是选项watch中的每一项的key。从Watcher构造函数中针对exporFn生成getter方法,之后会执行getter生成对应的value值。
而针对非函数的expOrFn会调用parsePath函数,实际上就是针对选项watch中生成watcher对象的处理,而这里就是watch原理的关键。
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原理就是利用已被拦截属性获取来实现的,基本过程如下:
选项watch监听原理的关键在于:
watch下key必须是被数据拦截的属性
如何实现watch监听?正是因为watch对应key是被数据拦截的属性,在选项watch下:
实际上所有监听原理的实现都是Dep对象与Watcher对象建立联系,在Dep对象对应属性改变时触发视图更新,从而运行相关逻辑。