watch是为vm的属性(已经在initData方法中被重写get和set方法)的get方法中多收集了一个watcherexpress
具体分析:闭包
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和handler,执行createWatcher 方法。oop
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) }
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(); } };
这里注意 new了一个Watcher,cb是以前的handler,expOrFn是以前的keythis
这个Watcher在new方法中是这样的spa
if (typeof expOrFn === 'function') { this.getter = expOrFn; } else { this.getter = parsePath(expOrFn); if (!this.getter) { this.getter = noop; warn( "Failed watching path: \"" + expOrFn + "\" " + 'Watcher only accepts simple dot-delimited paths. ' + 'For full control, use a function instead.', vm ); } } this.value = this.lazy ? undefined : this.get();
根据expOrFn,也就是key,提取watcher的核心:getter方法,这里expOrFn不是function而是一个key,那么进入parsePathprototype
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 } }
能够看出,根据key提取到的这个getter方法,实际上是key路径最后的那个属性的值。注意,平时咱们用watch,通常只监视vm对象的属性(好比叫name),可是其实key能够写成name.prop.prop……,如今简化说,key就是namecode
而后在new Watcher中,对象
this.value = this.lazy ? undefined : this.get();
因为laze为false,因此this.get()当即执行,
Watcher.prototype.get = function get () { pushTarget(this); var value; var vm = this.vm; try { value = this.getter.call(vm, vm); } catch (e) { if (this.user) { handleError(e, vm, ("getter for watcher \"" + (this.expression) + "\"")); } else { throw e } } finally { // "touch" every property so they are all tracked as // dependencies for deep watching if (this.deep) { traverse(value); } popTarget(); this.cleanupDeps(); } return value };
用vm调用getter方法,接着调用vm.name,也就是name的get方法,注意在vm._init方法中,initWatch是在initData以后,initData中defineReactive(vm.data)已经为data的全部可达属性重写了set和get方法,是能够收集watcher的。blog
get方法闭包中的dep收集这个新new出来的watcher,那么之后vm.name的set方法被调用的时候,就会经过dep.notify调用所收集的watcher的update方法,从而调用run方法,进而调用cb方法。ci