vue watch原理

2020-04-25  本文已影响0人  努力学习的小丸子

结论

1、很多情况下,computed和watch可以实现相同的功能;
2、当需要在数据变化时执行异步或开销较大的操作时,使用watch会更好一些。因为computed会立即返回,此时异步操作可能还没有完成;
3、因为数据是响应式的,使得watch有意义。并不是因为watch了才使得数据是响应式的。
4、使用immediate:true,会在初始化watch时就立即执行handler回调函数,而不用等下一次数据更新。
5、使用deep:true,才会递归监听对象的属性(如果监听的是对象或数组)。

过程描述

在created函数调用之前,调用了initWatcher方法,(调用该方法时,若immediate为真,则会立即执行回调函数),为每一个watcher属性实例化了一个Watcher,new Watcher ,会传入监听的属性key、回调函数和options,包括handler、deep和immediate的值,实例化的结尾会调用watcher.prototype.get方法,该方法会获得值并返回,值存在watcher.value属性上。
Get函数(即autorun)的执行即导致了watcher被收集为依赖。至此成功的监听了属性。
Get时,如果deep为真,则会递归监听所有的属性。

更新描述

在数据发生变化时,调用watcher.prototype.update方法,最终会执行第三步的get。
watch的watcher中的lazy和sync都为false,所以会执行queueWatcher.
第一步

function initWatch (vm, watch) {
    for (var key in watch) {
      var handler = watch[key];
//此处省略一些细节
     createWatcher(vm, key, handler);
    }
  }

第二步

Vue.prototype.$watch = function (
     expOrFn,
     cb,
     options
   ) {
     var vm = this;
     if (isPlainObject(cb)) {
       return createWatcher(vm, expOrFn, cb, options)
     }
     //watch属性的user设置为true
     options.user = true;
     var watcher = new Watcher(vm, expOrFn, cb, options);
     if (options.immediate) {
//cb就是watch的回调函数,当immediate为true时,立即调用一次
         cb.call(vm, watcher.value);
     }
     return function unwatchFn () {
       watcher.teardown();
     }
   };

第三步 watch的get方法

Watcher.prototype.get = function get () {
    //给全局变量Dep.target赋值
    pushTarget(this);
      value = this.getter.call(vm, vm);
//当deep为true时,会递归监听对象的所有属性
      if (this.deep) {
        traverse(value);
      }
      popTarget();
    }
    return value
  };

第四步 更新数据

Watcher.prototype.update = function update () {
    /* istanbul ignore else */
    console.error('watch-update!!!');
    if (this.lazy) {
//computed属性的watcher的lazy为true
      console.log('lazy...');
      this.dirty = true;
    } else if (this.sync) {
// //如果this.sync为true,则直接运行this.run获取结果 
      console.log('sync...');
      this.run();
    } else {
////this.sync为false,调用queueWatcher()函数把所有要执行update()的watch push到队列中
      console.log('other...');
      queueWatcher(this);
    }
  };

第五步

 Watcher.prototype.run = function run () {
    if (this.active) {
      var value = this.get();
      if (
        value !== this.value ||
        //对于引用类型,值相同,可能指向的内存地址不同
        isObject(value) ||
        this.deep
      ) {
        // set new value
        var oldValue = this.value;
        //其实执行的是 this.value=this.get();
        this.value = value;
          this.cb.call(this.vm, value, oldValue);
      }
    }
  };

问答

Q:哪些对象是Watcher?
A :在源码中,看到三个地方会初始化Watcher对象。挂载组件(mountComponent方法)、初始化watch(initWatch方法)和初始化computed(initComputed方法)。

上一篇 下一篇

猜你喜欢

热点阅读