watch选项的初始化也在 initState
中
function initState (vm) {
if (opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch);
}
}
initWatch
遍历传入的watch选项,逐个创建Watcher。 watch 的key 有几种传入方式
- 对象
{ handle, deep, immediate }
- methods中方法名
watch: { key: "handleWatch" }, methods: { handleWatch(){} }
- 直接传入的处理函数
watch: { key:function(){} }
- 以上类型组合的数组
watch: { key: [ {...}, fn, "handle"] }
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);
}
}
}
createWatcher
处理过后的handler始终是一个函数,调用$watch
为监听的属性创建一个Watcher
function createWatcher ( vm, expOrFn,handler,options) {
// 对象类型: 取对象的handler
if (isPlainObject(handler)) {
options = handler;
handler = handler.handler;
}
// handler是方法名,从实例上取得方法
if (typeof handler === 'string') {
handler = vm[handler];
}
return vm.$watch(expOrFn, handler, options)
}
vm.$watch
expOrFn
: 监听的属性名cb
: 上文传入的handler监听函数
创建Watcher
,过程中会读取 data 中相应的属性,从而触发属性的getter收集此Watcher
Vue.prototype.$watch = function ( expOrFn,cb,options) {
var vm = this;
options = options || {};
options.user = true;
// 读取属性,收集依赖
var watcher = new Watcher(vm, expOrFn, cb, options);
// 当设置了 immediate 属性,则现在就调用handler函数,而不是等数据变化时才触发
if (options.immediate) {
var info = "callback for immediate watcher \"" + (watcher.expression) + "\"";
pushTarget();
invokeWithErrorHandling(cb, vm, [watcher.value], vm, info);
popTarget();
}
return function unwatchFn () {
watcher.teardown();
}
};
简化版创建的Watcher
- expOrFn: 属性的key
- cb: handler函数
- options: {deep?,immediate?}
function Watcher (vm,expOrFn,cb,options) {
this.deep = options.deep
}
// handler函数
this.cb = cb;
// 传入的key或表达式
this.getter = expOrFn;
// 读取属性
this.value = this.get();
};
Watcher.prototype.get = function() {
// 将当前的 Watcher 推到全局位置,用于属性的dep收集它
pushTarget(this);
// 触发属性的getter并收集依赖
var value = this.getter.call(vm, vm);
// 如果传入了 deep 选项,则让该属性的所有嵌套属性均收集此Wather,该嵌套的任一属性发生变化都能收到更新回调
if (this.deep) {
traverse(value);
}
return value
};
监听的数据发生变化时,会将此数据收集的所有Watcher加入到更新队列中,在本轮事件循环的末尾,依次取出Watcher调用更新方法