浅析vue 函数配置项watch及函数 $watch 源码分享
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 };<br><br><br><br> 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方法,即实现监听
总结
以上所述是小编给大家介绍的vue 函数配置项watch及函数 $watch 源码分享,希望对大家有所帮助
上一篇: 在线心理测试,准到哭
下一篇: 性格心理测试看看你是什么性格