当前位置: 移动技术网 > IT编程>开发语言>JavaScript > vue 函数配置项watch以及函数 $watch 源码分享

vue 函数配置项watch以及函数 $watch 源码分享

2018年11月23日  | 移动技术网IT编程  | 我要评论
vue双向榜单的原理
 
 
大家都知道vue采用的是mvvm的设计模式,采用数据驱动实现双向绑定,不明白双向绑定原理的需要先补充双向绑定的知识,在watch的处理中将运用到vue的双向榜单原理,所以再次回顾一下:
vue的数据通过object.defineproperty设置对象的get和set实现对象属性的获取,vue的data下的数据对应唯一 一个dep对象,dep对象会存储改属性对应的watcher,在获取数据(get)的时候为相关属性添加具有对应处理函数的watcher,在设置属性的时候,触发def对象下watcher执行相关的逻辑
 
 
    // 为data的的所有属性添加getter 和 setter
    function definereactive( obj,key,val,customsetter,shallow
    ) {
        //
        var dep = new dep();

        /*....省略部分....*/
        var childob = !shallow && observe(val);  //为对象添加备份依赖dep
        object.defineproperty(obj, key, {
            enumerable: true,
            configurable: true,
            get: function reactivegetter() {
                var value = getter ? getter.call(obj) : val;
                if (dep.target) {
                    dep.depend();  // 
                    if (childob) {
                        childob.dep.depend(); //依赖dep 添加watcher 用于set ,array改变等使用
                        if (array.isarray(value)) {
                            dependarray(value);
                        }
                    }
                }
                return value
            },
            set: function reactivesetter(newval) {
                var value = getter ? getter.call(obj) : val;
                /* eslint-disable no-self-compare */
                if (newval === value || (newval !== newval && value !== value)) {
                    return
                }
                /* eslint-enable no-self-compare */
                if ("development" !== 'production' && customsetter) {
                    customsetter();
                }
                if (setter) {
                    setter.call(obj, newval);
                } else {
                    val = newval;
                }
                childob = !shallow && observe(newval);
                dep.notify();//有改变触发watcher进行更新
            }
        });
    }

  

在vue进行实例化的时候,将调用 initwatch(vm, opts.watch);进行初始化watch的初始化,该函数最终将调用 vm.$watch(exporfn, handler, options) 进行watch的配置,下面我们将讲解 vm.$watch方法
 
 
 vue.prototype.$watch = function (
            exporfn,
            cb,
            options
        ) {
            var vm = this;
            if (isplainobject(cb)) {
                return createwatcher(vm, exporfn, cb, options)
            }
            options = options || {};
            options.user = true;
            //为需要观察的 exporfn 添加watcher ,exporfn的值有改变时执行cb,
            //在watcher的实例化的过程中会对exporfn进行解析,并为exporfn涉及到的data数据下的def添加该watcher
            var watcher = new watcher(vm, exporfn, cb, options);

            //immediate==true 立即执行watch handler
            if (options.immediate) {  
                cb.call(vm, watcher.value);
            }

            //取消观察函数
            return function unwatchfn() {
                watcher.teardown();
            }
        };

  

来看看实例化watcher的过程中(只分享是观察函数中的实例的watcher)
 
 
 
var watcher = function watcher(
        vm,
        exporfn,
        cb,
        options,
        isrenderwatcher
    ) {
        this.vm = vm;
        if (isrenderwatcher) {
            vm._watcher = this;
        }
        vm._watchers.push(this);
        // options
        if (options) {
            this.deep = !!options.deep; //是否观察对象内部值的变化
            this.user = !!options.user;
            this.lazy = !!options.lazy;
            this.sync = !!options.sync;
        } else {
            this.deep = this.user = this.lazy = this.sync = false;
        }
        this.cb = cb; // 观察属性改变时执行的函数
        this.id = ++uid$1; // uid for batching
        this.active = true;
        this.dirty = this.lazy; // for lazy watchers
        this.deps = [];
        this.newdeps = [];
        this.depids = new _set();
        this.newdepids = new _set();
        this.expression = exporfn.tostring();
        // parse expression for getter
        if (typeof exporfn === 'function') {
            this.getter = exporfn;
        } else {
            // 将需要观察的数据:string | function | object | array等进行解析  如:a.b.c, 并返回访问该表达式的函数  
            this.getter = parsepath(exporfn);  
            if (!this.getter) {
                this.getter = function () { };
                "development" !== 'production' && warn(
                    "failed watching path: \"" + exporfn + "\" " +
                    'watcher only accepts simple dot-delimited paths. ' +
                    'for full control, use a function instead.',
                    vm
                );
            }
        }
        // this.get()将访问需要观察的数据 
        this.value = this.lazy
            ? undefined
            : this.get(); 
    };

    /**
     * evaluate the getter, and re-collect dependencies.
     */
    watcher.prototype.get = function get() {
        //this为$watch方法中实例化的watcher
        pushtarget(this);讲this赋给dep.target并缓存之前的watcher
        var value;
        var vm = this.vm;
        try {
            //访问需要观察的数据,在获取数据的getter中执行dep.depend();将$watch方法中实例化的watcher添加到对应数据下的dep中
            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(); //将之前的watcher赋给dep.target
            this.cleanupdeps();
        }
        return value
    };

 

 观察的对象改变时将执行该方法
watcher.prototype.run = function run() { /*....省略部分....*/     var value = this.get(); //重新获取info的值     var oldvalue = this.value; //保存老的值     this.value = value;     this.cb.call(this.vm, value, oldvalue); //执行watch的回调 /*....省略部分....*/   };

  

 

  以上代码在watcher实例化的时候执行  this.getter = parsepath(exporfn); 返回一个访问该属性的函数,参数为被访问的对象  如vm.$watch("info",function(new, old){console.log("watch success")});, this.getter =function(obj){return obj.info};,在执行watcher的get方法中,将执行value = this.getter.call(vm, vm);,触发属性的get方法,添加该watcher至info属性对应的def对象中,如果需要深度监听,将执行traverse(value),依次访问info(假设info只对象)对象下的属性,如果info的属性还有是对象的属性,将进行递归访问,以达到info以及info下所有的属性的def对象都会添加该watcher实例。

     当我们执行vm.info="change"时,将出发info的set方法,执行dep.notify();出发info所依赖的watcher执行watcher的run方法,即实现监听

 举例

 

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网