Vue2核心原理(简易版)-watch功能实现

Vue2核心原理(简易版)-watch功能实现

watch是用来作什么的?

Vue 经过 watch 选项提供了一个更通用的方法,来响应数据的变化。当须要在数据变化时执行异步或开销较大的操做时,这个方式是最有用的。vue

watch是什么?

一个对象,键是须要观察的表达式,值是对应回调函数。值也能够是方法名,或者包含选项的对象。Vue 实例将会在实例化时调用 $watch(),遍历 watch 对象的每个 property。文档传送git

示例:github

// 定义watchlet vm = new Vue({  el: '#app',  data() {return {      name: 'vue2'}
  },  watch: {name(newVal, oldVal) {      console.log(`watch name change form ${oldVal} to ${newVal}`);
    },
  }
})// 改变namesetTimeout(() => {
  vm.name = 'vue3'}, 1000)// 控制台打印// watch name change form vue2 to vue3复制代码

怎么作这个watch?

  1. 首先咱们要回顾vue依赖收集的原理和实现,请看个人前一篇文章,依赖收集

核心原理就是对传入Vue options的watch对象里面的全部的属性,都添加一个自定义watcher,收集对应属性的依赖,而后当这个依赖项更新的时候,不只通知它的渲染watcher(可能没有),也会通知当前的这个自定义watcher,从而叫到你定义的callback,完成函数内的逻辑。api

  1. 怎么拿到更新时的newValue和oldValue?

首先,watcher绑定的时候,默认要执行一遍this.get方法,这样咱们就完成了渲染watcher同样的行为,那就是'touch'到这个属性,让依赖项感知到了本身被依赖,添加订阅者,而且此时的值就是初始值,咱们把它记录到watcher上,this.value = this.get();。而后呢,每一次watcher更新调用update方法,咱们都会从新调用this.get,那么此时的返回值是否是就是最新值了呢?再加上咱们先前记录的this.value,很棒,咱们有了newValue和oldValue了!缓存

  1. 代码具体实现
    a. 初始化watch对象
    // 添加$watch mixinexport function stateMixin(Vue) {
      Vue.prototype.$watch = function (key, handler, options = {}) {const vm = this;
        options.user = true;new Watcher(vm, key, handler, options);
      };
    }// 对传入的watch添加自定义watcherfunction initWatch() {  const vm = this;  let watchs = vm.$options.watch;  for (let key in watchs) {let handler = watchs[key];if (Array.isArray(handler)) {      for (let i = 0; i < handler.length; i++) {
            createWatcher(vm, key, handler[i]);
          }
        } else {
          createWatcher(vm, key, handler);
        }
      }
    }function createWatcher(vm, key, handler) {
      vm.$watch(key, handler);
    }复制代码
    b. 修改Watcher类,应对自定义watcher属性的状况
    // observer/watcher.js constructor(vm, exprOrFn, cb, options) {  this.id = "watcher-" + id++;  this.vm = vm;  this.exprOrFn = exprOrFn;  this.cb = cb;  this.deps = [];  this.depsId = new Set();  this.options = options;  this.user = !!options.user;  if (typeof exprOrFn === "string") {this.getter = function () {      let path = exprOrFn.split(".");      let obj = path.reduce((pre, currentPath) => {return pre[currentPath];
          }, vm);      return obj;
        };
      } else {this.getter = exprOrFn;
      }  this.value = this.get();
    }   
    get() {
      pushTarget(this);  // 记录老的值
      const value = this.getter.call(this.vm);
      popTarget(this);  // 返回老的值
      return value;
    }   
    update() {  // vue中的更新操做是异步的
      // 屡次调用update 我但愿先将watcher缓存下来,等一会一块儿更新
      queueWatcher(this); 
    }run() {  const oldValue = this.value;  const newValue = this.get();  if (this.user) {this.cb(newValue, oldValue);// 别忘了把新的值记录为下次的老值this.value = newValue;
      }
    }复制代码

完整代码

github.com/Sotyoyo/do-…  分支do-watchapp

原文连接

若是你在非掘金地址看到这篇文章,这里有原文传送门,您的点赞是对我最大的支持!完异步

相关文章
相关标签/搜索