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

原文連接

若是你在非掘金地址看到這篇文章,這裏有原文傳送門,您的點贊是對我最大的支持!完異步

相關文章
相關標籤/搜索