Vue3为什么采用proxy实现数据监听

Veu3 采用proxy来实现数据监听 放弃了Object.defineProperty

我们都知道在Vue2 中采用了数据劫持结合发布者,订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的getter,setter,在数据变动的时候发布消息给订阅者,触发相应的监听回调。这个仍发是有缺点的,并不能实现数组和对象的部分监听,所以在最新的Vue3采用了Proxy,相比vue2的Object.defineProperty()能达到速度加倍、内存减半的效果。

首先看一下Vue init 过程
Vue初始化的过程分别是ObserverCompilerWatcher
当我们new Vue的时候,会调用Observer,通过Object.defineProperty便利vue对象的实力(data,computed,props(如果是组件的话))的所有属性进行监听,同事通过Compiler解析模板指令,解析到属性后就new 一个watcher并绑定更新函数到watcher当中,CompilerWatcher就通过属性进行关联。

当 Observer 中的 setter 检测到属性值改变的时候,就调用属性对应的所有 watcher 调用更新函数,从而更新到属性对应的 dom

来个简单的Object.defineProperty()例子

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <!-- 表单 -->
    <input type="text" id="input">
    <!-- 展示 -->
    <p id="desc"></p>
    <script>
        let obj = {};
        let temp = {};//采用临时变量代理obj
        Object.defineProperty(obj,'name',{
            //获取obj的name属性会触发
            get(){ 
                return temp['name'];
            },
            //给obj的name属性赋值会触发
            set(val){ 
                temp['name'] = val;//改变temp的结果
                input.value = val;//将值赋值到输入框
                desc.innerText = val; //将值显示到输入框下面
                //obj.name = val; //死循环,不能采取这种方式赋值,采用temp代理方式赋值和取值
            }
        });
        //设置了id值不需要document.getElementById()
        //调用上面的set方法,设置初始值
        obj.name = "";
        //调用上面的get方法,获取属性值并放到输入框
        input.value = obj.name;

        //输入框的变化时执行,这里不能使用箭头函数,因为箭头函数不绑定this,找的是上下文的this
        input.addEventListener('input',function(){
            //当值变化时会调用set方法
            obj.name = this.value;
        });
    </script>
</body>
</html>

效果如下:

在这里插入图片描述

从上面的例子可以知道:
1、Object.defineProperty 需要遍历所有的属性,这就造成了如果 vue 对象的 data/computed/props 中的数据规模庞大,那么遍历起来就会慢很多
2、同理,如果 vue 对象的 data/computed/props 中的数据规模庞大,那么 Object.defineProperty 需要监听所有的属性的变化,那么占用内存就会很大

再来看 Proxy

Proxy 对象用于定义基本操作的自定义行为(如属性查找,赋值,枚举,函数调用等)

语法:

const p = new Proxy(target,handler)

参数:
1、target:需要使用Proxy包装的目标对象(可以使任何类型的对象,包括原生数组,函数,甚至可以使一个代理)。
2、handler:一个对象,其属性是当执行一个操作是定义代理的行为的函数(可以理解为某种触发器)。具体的handler相关函数可以查询官网 handler相关函数

大家老看一个 proxy 例子

const obj = {
  name: 'Leo',
  age: 26,
  others: {
    mobile: 'mi10',
    watch: 'mi4'
  }
}
const p = new Proxy(obj, {
  get(target, key, receiver) {
    console.log('查看的属性为:' + key);
    return Reflect.get(target, key, receiver);
  },
  set(target, key, value, receiver) {
    console.log('设置的属性为:' + key);
    console.log('新的属性:' + key, '值为:' + value);
    Reflect.set(target, key, value, receiver);
  }
})
p.age = 22
console.log(p.age)
p.single = 'NO'
console.log(p.single)
p.others.shoe = 'boost'
console.log(p.others.shoe)

有上面可以知道,新增或更新属性并不会重新添加响应式处理,一样能坚挺到,因为Proxy是对对象的操作,会走到Proxy的逻辑中。

Proxy和Object.defineProperty()的区别

Object.defineProperty()
在data中定义数据:如 {num:3}, 是根据具体的属性去对get和set进行拦截:

Object.defineProperty(data, 'num', {
  get() {},
  set() {},
})

Proxy

Proxy 不需要关心key,它去拦截的是修改 data 上的任意 key 和 读取 data 上的任意 key
所以,不管是已有的 key 还是新增的 key,都会监听到
但是 Proxy 更加强大的地方还在于 Proxy 除了 get 和 set,还可以拦截更多的操作符

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值