Vue源碼之 watch

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

相關文章
相關標籤/搜索