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