watch是为vm的属性(已经在initData方法中被重写get和set方法)的get方法中多收集了一个watcher

 

 具体分析:

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 方法。

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是之前的key

这个Watcher在new方法中是这样的

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,那么进入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
        }
    }

可以看出,根据key提取到的这个getter方法,其实是key路径最后的那个属性的值。注意,平时我们用watch,一般只监视vm对象的属性(比如叫name),但是其实key可以写成name.prop.prop……,现在简化说,key就是name

 

然后在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的。

 

get方法闭包中的dep收集这个新new出来的watcher,那么以后vm.name的set方法被调用的时候,就会通过dep.notify调用所收集的watcher的update方法,从而调用run方法,进而调用cb方法。