浅聊vue双向绑定原理Object.defineProperty-/-Proxy

20 篇文章 0 订阅
13 篇文章 0 订阅
什么是双向绑定呢?vue又是怎么做的我们接下来就聊一聊
什么是双向绑定?

当数据模型data变化时,页面视图会得到响应更新

vue又是怎么做的?

vue其实现原理是对data的getter/setter方法进行拦截(Object.defineProperty或者Proxy),利用发布订阅的设计模式,在getter方法中进行订阅,在setter方法中发布通知,让所有订阅者完成响应。

说这些的时候我们在刚使用vue2.x的就会遇到过数据更新了啊,为何页面不更新呢。这其实就是Object.defineProperty在作祟。

而在vue3还没有发布时,很火的一个话题就是Vue3将使用Proxy 取代Vue2 版本的Object.defineProperty。那么Proxy对比Object.defineProperty有什么优势。

Proxy对比Object.defineProperty

Proxy

  • Proxy可以直接监听对象而非属性
  • Proxy可以直接监听数组的变化
  • Proxy有多达13种拦截方法,不限于apply、ownKeys、deleteProperty、has等等是Object.defineProperty不具备的
  • Proxy返回的是一个新对象,我们可以只操作新的对象达到目的,不需要像Object.defineProperty一样遍历每个属性有一定的性能提升
  • Proxy作为新标准将受到浏览器厂商重点持续的性能优化,也就是传说中的新标准的性能红利
  • Proxy直接实现对象属性的新增/删除

Object.defineProperty

  • Object.defineProperty只能劫持对象的属性,需要遍历对象的每一个属性,如果属性值也是对象,就需要递归进行深度遍历。

  • Object.defineProperty劫持的是对象的属性,所以新增属性时,需要重新遍历对象, 对其新增属性再次使用Object.defineProperty进行劫持。也就是Vue2.x中给数组和对象新增属性时,需要使用$set才能保证新增的属性也是响应式的, $set内部也是通过调用Object.defineProperty去处理的。

冷知识

Object.defineProperty无法监听数组数据的变化,但是为什么数组在使用push pop等方法的时候可以触发页面更新呢,那是因为vue内部拦截了这些方法。比如数组在使用push pop等方法的时候为什么可以触发页面更新呢,那是因为vue内部拦截了这些方法。

 // 重写push等方法,然后再把原型指回原方法
  var ARRAY_METHOD = [ 'push', 'pop', 'shift', 'unshift', 'reverse',  'sort', 'splice' ];
  var array_methods = Object.create(Array.prototype);
  ARRAY_METHOD.forEach(method => {
    array_methods[method] = function () {
      // 拦截方法
      return Array.prototype[method].apply(this, arguments);
    }
  });

回归正题我们分别用代码简单的实现下Proxy对比Object.defineProperty如何实现绑定的

Object.defineProperty实现

// 这是将要被劫持的对象
const data = {
  name: '',
};
// 遍历对象,对其属性值进行劫持
Object.keys(data).forEach(function(key) {
  console.log('1')
  Object.defineProperty(data, key, {
    enumerable: true,
    configurable: true,
    get: function(newVal) {
      return val
    },
    set: function(newVal) {
      // 当属性值发生变化时我们可以进行额外操作
      console.log(`大家好,我是${newVal}我被劫持了`);
      val = newVal;
    },
  });
});
data.name = '111';

Proxy实现

const target = {
  name: '控制'
};

const handler = {

  get: function(target, key) {
    console.log(`${key} 被读取`);
    return target[key];
  },

  set: function(target, key, value) {
    console.log(`${key} 被设置为 ${value}`);
    target[key] = value;
  }
};

const testObj = new Proxy(target, handler);

console.log(testObj.name); // name 被读取 及输出名字 控制
testObj.name = 1; // name 被设置为 1  输出 1

Proxy参数介绍

1.get(target, propKey, receiver)

该方法的含义是:用于拦截某个属性的读取操作。它有三个参数,如下解析:

  • target: 目标对象。
  • propKey: 目标对象的属性。
  • receiver: (可选),该参数为上下文this对象
2.set(target, propKey, value, receiver)

该方法是用来拦截某个属性的赋值操作,它可以接受四个参数,参数解析分别如下:

  • target: 目标对象。
  • propKey: 目标对象的属性名
  • value: 属性值
  • receiver(可选): 一般情况下是Proxy实列
3.has(target, propKey)

该方法是判断某个目标对象是否有该属性名。接收二个参数,分别为目标对象和属性名。返回的是一个布尔型。

  • target: 目标对象。
  • propKey: 目标对象的属性名
4.construct(target, args, newTarget)

该方法是用来拦截new命令的,它接收三个参数,分别为 目标对象,构造函数的参数对象及创造实列的对象。
第三个参数是可选的。它的作用是拦截对象属性。

  • target: 目标对象。
  • args: 构造函数的参数对象
  • newTarget:创造实列的对象
5.apply(target, object, args)

该方法是拦截函数的调用的。该方法接收三个参数,分别是目标对象。目标对象上下文this对象 和 目标对象的数组;它和 Reflect.apply参数是一样的
第三个参数是可选的。它的作用是拦截对象属性。

  • target: 目标对象。
  • object: 目标对象上下文this对象
  • args:目标对象的数组

写在最后的话大家不要忘记,点赞,评论,收藏

请添加图片描述

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值