vue3响应式原理:Proxy + Reflect

vue3响应式原理:Proxy + Reflect,代码示例

 // 目标对象
   const user = {
    
    
       name: '小小',
       age: 20,
       wife: {
    
    
           name: '小明',
           age: 19
       }
   }

   // 把目标对象变成代理对象
   const proxyUser = new Proxy(user, {
    
    
       // 获取
       get(target, prop) {
    
    
           console.log('get方法调用了')
           console.log(target)
           return Reflect.get(target, prop)

       },
       // 新增、修改
       set(target, prop, val) {
    
     
           console.log('set方法调用了')
           return Reflect.set(target, prop, val)

       },
       // 删除
       deleteProperty(target, prop) {
    
    
           console.log('delete方法调用了')
           return Reflect.deleteProperty(target, prop)

       }
   })

   // 触发get
   console.log(proxyUser.name)
   // 触发set
   proxyUser.age += 1;
   proxyUser.gender = 'man'
   // 触发delete
   delete proxyUser.name
   // 更新目标对象中的某个属性对象中的属性值
   proxyUser.wife.name = "天天天三百"
   console.log(user)

Reflect学习

1、Reflect是什么

Reflect 是一个window 内置的一个全局对象,它提供拦截 JavaScript 操作的方法

与大多数全局对象不同,Reflect并非一个构造函数,所以不能通过new 运算符对其进行调用,或者将Reflect对象作为一个函数来调用。Reflect的所有属性和方法都是静态的(就像Math对象)

2、Reflect常用的静态方法

2.1、Reflect.get(target, propertyKey[, receiver]) ---- 从一个对象中取属性值
  • target: 需要取值的目标对象
  • propertyKey: 需要获取的值的键值
  • receiver:receiver则为getter调用时的this值。

返回值:属性的值

// Object
var obj = {
    
     x: 1, y: 2 };
Reflect.get(obj, "x"); // 1

// Array
Reflect.get(["zero", "one"], 1); // "one"

// Proxy with a get handler
var x = {
    
    p: 1};
var obj = new Proxy(x, {
    
    
  get(t, k, r) {
    
     return k + "bar"; }
});
Reflect.get(obj, "foo");   // "foobar"
2.2、Reflect.set(target, propertyKey, value[, receiver]) ---- 为对象设置或修改属性值
  • target : 设置属性的目标对象。
  • propertyKey : 设置的属性的名称。
  • value : 设置的值。
  • receiver : receiver则为setter调用时的this值。

返回值 : 返回一个 Boolean 值,表明是否成功设置属性。

// Object
var obj = {
    
    };
Reflect.set(obj, "prop", "value"); // true
obj.prop; // "value"

// Array
var arr = ["duck", "duck", "duck"];
Reflect.set(arr, 2, "goose"); // true
arr[2]; // "goose"

Reflect.set(arr, "length", 1); // true
arr; // ["duck"];
2.3、Reflect.deleteProperty(target, propertyKey) ---- 用于删除属性
  • target : 删除属性的目标对象。
  • propertyKey : 需要删除的属性的名称

返回值 : 返回一个 Boolean 值,表明该属性是否被成功删除.

var obj = {
    
     x: 1, y: 2 };
Reflect.deleteProperty(obj, "x"); // true
obj; // { y: 2 }

var arr = [1, 2, 3, 4, 5];
Reflect.deleteProperty(arr, "3"); // true
arr; // [1, 2, 3, , 5]
arr[3]  // undefined 

// 如果属性不存在,返回 true
Reflect.deleteProperty({
    
    }, "foo"); // true

// 如果属性不可配置,返回 false
Reflect.deleteProperty(Object.freeze({
    
    foo: 1}), "foo"); // false
2.4、Reflect.has(target, propertyKey) ----- 需要检查目标对象是否存在此属性。
  • target : 目标对象。
  • propertyKey : 属性名

返回值 : 返回一个 Boolean 值,表示是否存在此属性

Reflect.has({
    
    x: 0}, "x"); // true
Reflect.has({
    
    x: 0}, "y"); // false

// 如果该属性存在于原型链中,返回 true
Reflect.has({
    
    x: 0}, "toString");
2.5、Reflect.apply(target,thisArgument,argumentsList)
  • target: 目标函数。
  • thisArgument: target 函数调用时绑定的 this 对象。
  • argumentsList: target 函数调用时传入的实参列表,该参数应该是一个类数组的对象。
Reflect.apply(Math.floor, undefined, [1.75]);    // 1
Reflect.apply("".charAt, "ponies", [3]);   // "i"
2.6、其它方法,参考地址:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect/deleteProperty

注意:Reflect 不支持 ie浏览器,所以Vue3不支持ie
在这里插入图片描述

Vue3中的 Proxy + Reflect 解决了 Vue2 Object.defineProperty中遇到的哪些问题

在使用Object.defineProperty的时候,我们遇到的问题有:

1.一次只能对一个属性进行监听,需要遍历来对所有属性监听。这个我们在上面已经解决了。
2. 在遇到一个对象的属性还是一个对象的情况下,需要递归监听。
3. 对于对象的新增属性,需要手动监听
4. 对于数组通过push、unshift方法增加的元素,也无法监听

这些问题在Proxy中都轻松得到了解决。

Vue3的响应式,为什么 Proxy 要配合 Reflect 一起使用,不能直接使用target[key]返回

①保证上下文环境中的 this, 指向的是代理后的proxy对象

举个例子

const target = {
    
    
    foo: 24,
    get bar () {
    
    
        return this.foo
    }
}

const observed = reactive(target)

此时,如果不用 Reflect.get,而是 target[key],那么这里上下文中的 this 就指向target,而不是 observed

那么,之后你通过this去访问数据时,你访问的就是代理之前的对象,而不是代理之后的Proxy对象。这样用户对对象的操作你就无法监听到了,就无法触发回调,进行其它操作了;

而Reflect的get、set方法中的第三个参数receiver,就是修改this指向的;所以,我们要通过 Reflect 中的get、set方法将上下文中的 this 指向代理对象。这样的话,之后访问数据时,访问的就是Proxy代理后的对象了,同时也就会触发Proxy对象中的get、set方法了,从而也就能触发回调,进行其它操作了;

猜你喜欢

转载自blog.csdn.net/yiyueqinghui/article/details/126391362