手摸手從0實現簡版Vue ---(watch實現)

接:javascript

手摸手從0實現簡版Vue --- (對象劫持)vue

手摸手從0實現簡版Vue --- (數組劫持)java

手摸手從0實現簡版Vue --- (模板編譯)git

手摸手從0實現簡版Vue --- (依賴收集)github

手摸手從0實現簡版Vue --- (批量更新&nextTick)數組

watch的兩種用法

使用watch有兩種方法,第一種直接調用vm.$watch,第二種是在選項中配置watch屬性。異步

watch: {
  msg: {
    handler: function (newValue, oldValue) {
      console.log('watch:', {
        newValue,
        oldValue
      })
    },
    immediate: true
  }
}

// or

vm.$watch('msg', function(newVal, oldVal) {
  console.log({ newVal, oldVal })
})
複製代碼

咱們要去實現一個vm.$watch方法,$watch方法的話作了兩件事:函數

  1. userDef 中分離 handler 和 其餘的 opts
  2. new一個Watcher,而且增長 { user: true } 標記爲用戶watcher。

下面看代碼:post

Vue.prototype.$watch = function(expr, userDef) {
  const vm = this;
  let handler = userDef;
  const opts = { user: true }
  if (userDef.handler) {
    handler = userDef.handler;
    Object.assign(opts, userDef);
  }
  new Watcher(vm, expr, handler, opts);
}
複製代碼

Watcher 內部實現

首先把傳入的字符串作爲函數返回,例如'msg'轉化爲 util.getValue(vm, 'msg')ui

這一步很是關鍵,由於new Watcher的時候默認調用一次get方法,而後執行getter函數,這個過程會觸發msggetter,讓msgdep添加一個用戶watcher,完成依賴收集。

constructor(vm, exprOrFn, cb = () => {}, opts = {}) {
    this.vm = vm;
    this.exprOrFn = exprOrFn;
    if (typeof exprOrFn === 'function') {
      this.getter = exprOrFn;
    } else if (typeof exprOrFn === 'string') {
+      // 用戶watcher
+      // 解析expr,取到data上的值
+      // 取值的時候完成依賴收集
+      this.getter = function () {
+        return util.getValue(vm, exprOrFn);
+      }
+    }
+   this.immediate = opts.immediate
+    // 用戶添加的watcher,標記一下
+    this.user = opts.user
    this.cb = cb;
    this.opts = opts;
    this.id = id++;
    this.deps = [];
    this.depsId = new Set();
    // 咱們但願在回調函數中返回一個新值,一箇舊值,因此咱們須要記錄getter返回的值 
+    this.value = this.get();
+    if (this.immediate) {
+      this.cb(this.value)
+    }
  }
複製代碼

完成依賴收集後,當咱們的數據發生變化後,調用Watcherrun方法,進行值的比對,若是發生變化,就去執行這個watchercallback

class Watcher {
  run() {
    const newValue = this.get();
    if (newValue !== this.value) {
      this.cb(newValue, this.value);
      this.value = newValue;
    }
  }
}
複製代碼

這樣的話,咱們的 $watch 也就實現了,下面咱們去實現initWatch方法,遍歷下用戶傳入的watch配置,進行watcher添加:

function initWatch(vm) {
  const watch = vm.$options.watch;
  for (const key in watch) {
    const userDef = watch[key];
    createWatcher(vm, key, userDef);
  }
} 
複製代碼

此時咱們使用最開始的兩種方法去監聽咱們的msg值的變化,而後異步去更新一下咱們的msg值,兩個log都會正常執行了。

這樣的話咱們的watch也就簡單實現啦~

代碼點擊=> 傳送門

相關文章
相關標籤/搜索