zoukankan      html  css  js  c++  java
  • Vue源码之 watch

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

  • 相关阅读:
    mybatis实战教程(mybatis in action),mybatis入门到精通
    jquery 设置select的默认值
    一些最佳做法,即将推出的产品列表
    My97DatePicker日历控件日报、每周和每月的选择
    Android在第三方应用程序系统应用尽早开始,杀死自己主动的第三方应用程序,以重新启动
    Scrapy研究和探索(五岁以下儿童)——爬行自己主动多页(抢别人博客所有文章)
    Arcgis sde 10.1您不能创建在安装后的空间库,提示User has privileges required to create database objects.
    cocos2d-x 网络请求
    HDU 3729 I&#39;m Telling the Truth(二部图最大匹配+结果输出)
    解决opengl计算顶点的法线问题
  • 原文地址:https://www.cnblogs.com/chuliang/p/11093720.html
Copyright © 2011-2022 走看看