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

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