视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
浅析vue 函数配置项watch及函数 $watch 源码分享
2020-11-27 22:03:38 责编:小采
文档

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 源码分享,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!

下载本文
显示全文
专题