Vue双向数据绑定 深入响应式原理

1.ProxyReflect 

Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。

代理是什么:目标对象的抽象,即允许我们拦截并重新定义对一个对象的基本操作

怎么创建:使用Proxy构造函数创建

目的:定义捕获器(trap)

捕获器(trap)

什么是捕获器:在处理程序对象中——基本操作的拦截器;

在操作系统中——程序流中的一个同步中断

const target = {
  id: "target",
}; //目标对象
const handler = {};//是一组trap(get(){},set(){})
const proxy = new Proxy(target, handler); //代理对象
console.log(target.id === proxy.id); //true
console.log(target === proxy); //false
//目标对象、代理对象共享属性
target.id = "foo";
console.log("原对象:", target.id, " 代理对象:", proxy.id);
proxy.id = "bar";
console.log("原对象:", target.id, " 代理对象:", proxy.id);
console.log("原型:", Proxy.prototype); //undefined

原型详解     js 原型链,继承详解

const target = {
  id: "target",
}; //目标对象
const handler = {
  get(trapTarget, property, receiver) {
    // console.log(trapTarget === target); //true
    // console.log(receiver === proxy); //true
    return trapTarget[property];
  }, //目标对象,要查询的属性,代理对象
};
const proxy = new Proxy(target, handler); //代理对象
console.log(target.id, proxy.id); //target target

 

 处理程序中所有可捕获的方法都有对应的反射(Reflect)API方法

const target = {
  id: "target",
};
const handler = {
  //   get() {
  //     return Reflect.get(...arguments);
  //   },
  get: Reflect.get,
};
const proxy = new Proxy(target, handler);
console.log(target.id, proxy.id); //target target
const target = {
  id: "target",
};
const proxy = new Proxy(target, Reflect);
console.log(target.id, proxy.id); //target target
console.log(Reflect.get(target, "id")); //target

2.Object.defineProperty

 Object.defineProperty() 允许精确地添加或修改对象上的属性。

       const object1 = {};
        Object.defineProperty(object1, 'property1', {
            value: 37,
            enumerable: true,
            configurable: true,
            writable: true,
        });
        // 默认情况下,它们不可枚举、不可配置、不可写
        console.log(object1.property1);

Vue3为什么用Proxy代替Object.defineProperty

Vue2.js中的响应式数据是基于 Object.defineProperty()实现的 

Vue3.js中的响应式数据是基于Proxy实现的

相同点:

  1. 都可以用来修改对象属性的行为。
  2. 都可以用来实现数据劫持,提供get、set方法。
  3. 都可以用来实现对象的代理。

不同点:

  1.  Object.defineProperty()只能监听对象属性的读取和写入操作,而Proxy可以监听对象属性的更多操作,比如in运算符、delete运算符等。
  2.  Object.defineProperty()无法检测到以下变化: property 的添加或移除;利用索引直接设置一个数组项;修改数组的长度。而Proxy可以监听。
  3. var vm = new Vue({
      data:{
        a:1
      }
    })
    // `vm.a` 是响应式的
    vm.b = 2// `vm.b` 是非响应式的
    
    //响应式的
    Vue.set(vm.someObject, 'b', 2)
    this.$set(this.someObject,'b',2)
    var vm = new Vue({
      data: {
        items: ['a', 'b', 'c']
      }
    })
    vm.items[1] = 'x' // 不是响应性的
    vm.items.length = 2 // 不是响应性的
    
    //响应性的
    // Vue.set
    Vue.set(vm.items, indexOfItem, newValue)
    vm.$set(vm.items, indexOfItem, newValue)
    
    // Array.prototype.splice
    vm.items.splice(indexOfItem, 1, newValue)
    vm.items.splice(newLength)
  4.  Object.defineProperty()只能监听单个属性的变化,而Proxy可以监听整个对象的变化。
  5.  Object.defineProperty()需要知道要监听的属性名,而Proxy可以通过Reflect对象拦截所有属性操作。
  6.  Object.defineProperty()不支持撤销监听,而Proxy可以使用Proxy.revocable()方法创建一个可撤销的代理对象。

Vue2中,后添加的数据不是响应式的,由于 Vue 会在初始化实例时对 property 执行 getter/setter 转化,所以 property 必须在 data 对象上存在才能让 Vue 将它转换为响应式的。解决方法:

(1)对于数组来说,只有pop、push、shift、unshit、sort、splice、reverse等改变原数组的方法能被监听到(Vue 将被侦听的数组的变更方法进行了包裹,所以它们也将会触发视图更新。)

Vue.set( target, propertyName/index, value )(2)Vue.set( target, propertyName/index, value )

向响应式对象中添加一个 property,并确保这个新 property 同样是响应式的,且触发视图更新。它必须用于向响应式对象上添加新 property,因为 Vue 无法探测普通的新增 property (比如 this.myObject.newProperty = 'hi')

注意对象不能是 Vue 实例(vm),或者 Vue 实例的根数据对象(vm._data)。

3.ref 和 reactive ——原始值与非原始值的响应方案

ref是通过一个中间对象RefImpl持有数据,并通过重写它的set和get方法实现数据劫持的,本质上依旧是通过Object.defineProperty()RefImplvalue属性进行劫持。

reactive则是通过Proxy进行劫持的。Proxy无法对基本数据类型进行操作,进而导致reactive在面对基本数据类型时的束手无策。 

Vue 3 还提供了 toRefs 函数,它可以将一个 reactive 对象的所有属性转换为 ref 对象,并返回一个响应式对象,其中每个属性都是一个 ref。这在需要将 reactive 对象的属性传递给其他组件或函数时特别有用,因为它允许你保持响应式并避免手动解包 .value

import { ref,reactive } from 'vue';
const msg =ref('hello')
const msg1 =ref({key:'hello'})
const msg2 =reactive({key:'hello'})
console.log(msg)
console.log(msg1)
console.log(msg2)

  1. ref可以存储原始类型(Number、Boolean、String),若存储对象类型,是对reactive的封装。
  2. ref需要通过.value访问数据。
  3. 可以重新分配一个全新的对象给refvalue属性,而reactive()不能。
  4. ref()返回的是一个持有原始数据的RefImpl实例,而reactive()返回的类型则是原始数据的代理Proxy实例。
  5. watch默认只观察refvalue,而对reactive则执行深度监听。
  6. ref默认会用reactive对象类型的原始值进行深层响应转换。

4.v-bind和v-model

  • v-bind指令单向绑定,数据影响视图。v-bind:title="message"简写 :title="message"
  • v-model指令双向绑定,只能用于表单元素(如input、select、textarea等,输入元素,有value值)。v-model:value="name" 简写 v-model="name"
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值