利用 Proxy API 實現一個簡易 MVVM

vue 3 使用了 proxy api,有些手癢,就弄一個簡單的結構玩玩吧。Proxy API 見 MDN Proxy

效果圖

思路

依賴收集:Mvvm 初始化時劫持數據,並設置觀察者 dep。模仿 vue 結構是在 get 時往觀察者 dep 推入被觀察者 watcher,然後 set 時讓觀察者通知所有被觀察者開始更新。

數據響應:這裏只是簡單在 compiler 裏面去掃描了一遍所有帶着 v-text 和 v-model 鉤子的標籤做了處理:定義被觀察者 watcher,被觀察者的觸發函數寫上節點 DOM 的更新。

當然我們知道 vue 的數據響應過程比這個複雜多了,有着虛擬 DOM 和複雜的 diff 算法。

代碼

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <div id="app">
    <div  v-text="text1"></div>
    <input type="text"  v-model="text1">

    <div  v-text="text2"></div>
    <input type="text" v-model="text2">
  </div>

  <script>
    class Watcher {
      constructor(cb) {
        this.cb = cb;
      }
      run() {
        this.cb();
      }
    }
    class Dep {
      constructor() {
        this.subs = [];
        this.target = null;
      }
      notify() {
        this.subs.forEach(item => {
          item.run();
        })
      }
    }
    class Mvvm {
      constructor(data) {
        let that = this;
        this.dep = new Dep();
        this.data = new Proxy(data, {
          get(obj, key, prox) {
            that.dep.target && that.dep.subs.push(that.dep.target);
            return obj.data[key];
          },
          set(obj, key, value, prox) {
            obj.data[key] = value;
            that.dep.notify();
            return true;
          }
        });
        this.compiler();
      }
      compiler() {
        let that = this;
        let app = document.getElementById('app');
        let bindTextNodes = app.querySelectorAll('[v-text]');
        let bindInputNodes = app.querySelectorAll('[v-model]');

        bindTextNodes.forEach(bindTextNode => {
          let textModel = bindTextNode.getAttribute('v-text');
          let watcher = new Watcher(function() {
            bindTextNode.innerText = that.data[textModel];
          });
          that.dep.target = watcher;
          bindTextNode.innerText = that.data[textModel];
          this.dep.target = null;
        })

        bindInputNodes.forEach(bindInputNode => {
          let inputModel = bindInputNode.getAttribute('v-model');
          let watcher = new Watcher(function() {
            bindInputNode.value = that.data[inputModel];
          });
          this.dep.target = watcher;
          bindInputNode.value = that.data[inputModel];

          bindInputNode.addEventListener('input', function(evt) {
            that.data[inputModel] = evt.target.value;
          })
          that.dep.target = null; 
        })
      }
    }

    new Mvvm({
      data: {
        text1: '123',
        text2: '789'
      }
    })
  </script>
</body>
</html>

爲什麼時 Proxy 而不是 Object.defineProperty ?

我們知道 Vue2.0 的反應系統是使用 Object.defineProperty 的 getter 和 setter。 但是,Vue 3 將使用 ES2015 Proxy 作爲其觀察者機制。 這消除了以前存在的警告,使速度加倍,並節省了一半的內存開銷。

而爲什麼使用 Proxy 替代 Object.defineProperty?Proxy 可以劫持整個對象,並返回一個新的對象。Proxy 不僅可以代理對象,還可以代理數組,還可以代理動態增加的屬性。節省內存,速度加倍。

爲了繼續支持 IE11,Vue 3 將發佈一個支持舊觀察者機制和新 Proxy 版本的構建

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章