20190521 基於數據劫持的雙向綁定方法 Object.defineProperty 與 Proxy

雙向綁定的方法

  • KnockoutJS 基於觀察者模式(發佈-訂閱)的雙向綁定
  • Ember 基於數據模型的雙向綁定
  • Angular 基於髒檢查的雙向綁定
  • 基於數據劫持的雙向綁定 (重點講解)
    • Object.defineProperty
    • Proxy

數據劫持

數據劫持的優點

  • 不需要顯示的調用

    比如:Vue 利用數據劫持+發佈訂閱,可以直接通知變化並且驅動視圖

  • 可以精確得知變化數據

    我們劫持了屬性setter,當屬性值改變我們可以精確的獲取變化的內容 newVal,不需要額外的 diff 操作

數據劫持雙向綁定的實現思路

  • 利用 ProxyObject.defineProperty 生成的 Observer 針對對象/對象的屬性進行劫持,在屬性發生變化時通知訂閱者
  • 解析器 Compile 會解析模板中的指令,收集指令依賴的數據和方法,等待數據變化,然後進行渲染
  • Watcher 接收 Observer 產生的數據變化,根據 Compile 提供的指令進行視圖渲染,使得數據變化促使視圖更新

基於 Object.defineProperty 雙向綁定的特點

利用 Object.defineProperty 方法劫持對象的訪問器,在 屬性值 發生變化的時候獲取到變化,並且可以進一步操作。

// 這是將要被劫持的對象
const data = {
  name: ""
};

function say(name) {
  switch (name) {
    case "a":
      console.log("my name is a");
      break;
    case "b":
      console.log("my name is b");
      break;
    case "c":
      console.log("my name is c");
      break;
    default:
      console.log("my name is none");
      break;
  }
}

// 遍歷對象,對其屬性值進行劫持
Object.keys(data).forEach(function(key) {
  Object.defineProperty(data, key, {
    enumerable: true,
    configurable: true,
    get: function() {
      console.log("get");
    },
    set: function(newVal) {
      // 當屬性值發生變化時我們可以進行額外操作
      console.log(`大家好,我係${newVal}`);
      say(newVal);
    }
  });
});

data.name = "a";
//大家好,我係a
//my name is a

案例地址:https://codepen.io/xiaomuzhu/pen/jxBRgj/?editors=1010

基於 Proxy 實現的雙向綁定的特點

const input = document.getElementById("input");
const p = document.getElementById("span");
const obj = {};

const newObj = new Proxy(obj, {
  get: function(target, key, receiver) {
    console.log(`getting ${key}!`);
    return Reflect.get(target, key, receiver);
  },
  set: function(target, key, value, receiver) {
    console.log(target, key, value, receiver);
    // 第一次打印:{} "text" "f" Proxy {}
    // 第二次打印:{text: "f"} "text" "ff" Proxy {text: "f"}
    if (key === "text") {
      input.value = value;
      p.innerHTML = value;
    }
    return Reflect.set(target, key, value, receiver);
  }
});

input.addEventListener("keyup", function(e) {
  newObj.text = e.target.value;
});

案例地址:https://codepen.io/xiaomuzhu/pen/zjwGoN/

對比兩種雙向綁定

Object.defineProperty 的缺陷

  • 無法監聽數組變化

    Vue 的文檔提到了 Vue 是可以檢測到數組變化的,但是隻有以下八種方法:push pop shift unshift splice sort reverse ,vm.items[indexOfItem] = newValue 這種是無法檢測的。

  • 只能劫持對象的屬性

    只能劫持對象的屬性,因此我們需要對每個對象的每個屬性進行遍歷,如果屬性值也是對象那麼需要深度遍歷,顯然能劫持一個完整的對象是更好的選擇

  • 需要 hack

Proxy 的優點

  • Proxy 可以直接監聽數組的變化
  • Proxy 返回的是一個新對象,我們可以只操作新的對象達到目的,而非屬性
  • Proxy 支持的攔截操作一覽,一共 13 種,比如:has、apply、construct、deleteProperty…
  • 兼容性問題

瞭解Proxy更多詳情:https://blog.csdn.net/m_review/article/details/90443242

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