• defineProperty 和 proxy 的区别


    1. defineProperty 和 proxy 的区别

    1. Vue2.x 通过给每个对象添加getter setter属性去改变对象,实现对数据的观测,Vue3.x 通过 Proxy 代理目标对象,且一开始只代理最外层对象,嵌套对象lazy by default ,性能会更好
    2. 支持数组索引修改,对象属性的增加,删除

    Vue 在实例初始化时遍历 data 中的所有属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter。这样当追踪数据发生变化时,setter 会被自动调用。

    Object.defineProperty 是 ES5 中一个无法声明的特性,这也就是 Vue 不支持 IE8 以及更低版本浏览器的原因。

    但是这样做有以下问题:

    1. 添加或删除对象的属性时,Vue 检测不到。因为添加或删除的对象没有在初始化进行响应式处理,只能通过$set 来调用Object.defineProperty()处理。
    2. 无法监控到数组下标和长度的变化。

    Vue3 使用 Proxy 来监控数据的变化。Proxy 是 ES6 中提供的功能,其作用为:用于定义基本操作的自定义行为(如属性查找,赋值,枚举,函数调用等)。相对于Object.defineProperty(),其有以下特点:

    1. Proxy是对整个对象的代理,而Object.defineProperty只能代理某个属性。
    2. 对象上新增属性,Proxy可以监听到,Object.defineProperty不能。
    3. 数组新增修改,Proxy可以监听到,Object.defineProperty不能。
    4. 若对象内部属性要全部递归代理,Proxy可以只在调用的时候递归,而Object.definePropery需要一次完成所有递归,性能比Proxy差。
    5. Proxy不兼容IE,Object.defineProperty不兼容IE8及以下
    6. Proxy使用上比Object.defineProperty方便多。

    Vue3.x 改用 Proxy 替代 Object.defineProperty。因为 Proxy 可以直接监听对象和数组的变化,并且有多达 13 种拦截方法。

    相关代码如下参考资料:

    一文搞懂 Object.defineProperty 和 proxy

    Object 和 proxy 区别

    1.1. Object.defineProperty 介绍?

     Object.defineProperty 函数一共有三个参数,第一个参数是需要定义属性的对象,第二个参数是需要定义的属性,第三个是该属性描述符。
     一个属性的描述符有四个属性,分别是 value 属性的值,writable 属性是否可写,enumerable 属性是否可枚举,configurable 属性是否可配置修改。
    
    • 1
    • 2

    整体思路是数据劫持 + 观察者模式

    对象内部通过 defineReactive 方法,使用 Object.defineProperty 将属性进行劫持(只会劫持已经存在的属性),数组则是通过重写数组方法来实现。当页面使用对应属性时,每个属性都拥有自己的 dep 属性,存放他所依赖的 watcher(依赖收集),当属性变化后会通知自己对应的 watcher 去更新 (派发更新)。

    相关代码如下

     class Observer {
       // 观测值
       constructor(value) {
         this.walk(value);
       }
       walk(data) {
         // 对象上的所有属性依次进行观测
         let keys = Object.keys(data);
         for (let i = 0; i < keys.length; i++) {
           let key = keys[i];
           let value = data[key];
           defineReactive(data, key, value);
         }
       }
     }
     // Object.defineProperty数据劫持核心 兼容性在ie9以及以上
     function defineReactive(data, key, value) {
       observe(value); // 递归关键
       // --如果value还是一个对象会继续走一遍odefineReactive 层层遍历一直到value不是对象才停止
       //   思考?如果Vue数据嵌套层级过深 >>性能会受影响
       Object.defineProperty(data, key, {
         get() {
           console.log("获取值");
     ​
           //需要做依赖收集过程 这里代码没写出来
           return value;
         },
         set(newValue) {
           if (newValue === value) return;
           console.log("设置值");
           //需要做派发更新过程 这里代码没写出来
           value = newValue;
         },
       });
     }
     export function observe(value) {
       // 如果传过来的是对象或者数组 进行属性劫持
       if (
         Object.prototype.toString.call(value) === "[object Object]" ||
         Array.isArray(value)
       ) {
         return new Observer(value);
       }
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    响应式数据原理详解 传送门

    详细资料可以参考: 《Object.defineProperty()》

    1.2. 使用 Object.defineProperty() 来进行数据劫持有什么缺点?

     有一些对属性的操作,使用这种方法无法拦截,比如说通过下标方式修改数组数据或者给对象新增属性,vue 内部通过重写函数解决了这个问题。在 Vue3.0 中已经不使用这种方式了,而是通过使用 Proxy 对对象进行代理,从而实现数据劫持。使用 Proxy 的好处是它可以完美的监听到任何方式的数据改变,唯一的缺点是兼容性的问题,因为这是 ES6 的语法。
    
    • 1

    1.3. 什么是 Proxy ?

     Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”,即对编程语言进行编程。
     ​
     Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。
    
    • 1
    • 2
    • 3
     import { mutableHandlers } from "./baseHandlers"; // 代理相关逻辑
     import { isObject } from "./util"; // 工具方法
     ​
     export function reactive(target) {
       // 根据不同参数创建不同响应式对象
       return createReactiveObject(target, mutableHandlers);
     }
     function createReactiveObject(target, baseHandler) {
       if (!isObject(target)) {
         return target;
       }
       const observed = new Proxy(target, baseHandler);
       return observed;
     }
     ​
     const get = createGetter();
     const set = createSetter();
     ​
     function createGetter() {
       return function get(target, key, receiver) {
         // 对获取的值进行放射
         const res = Reflect.get(target, key, receiver);
         console.log("属性获取", key);
         if (isObject(res)) {
           // 如果获取的值是对象类型,则返回当前对象的代理对象
           return reactive(res);
         }
         return res;
       };
     }
     function createSetter() {
       return function set(target, key, value, receiver) {
         const oldValue = target[key];
         const hadKey = hasOwn(target, key);
         const result = Reflect.set(target, key, value, receiver);
         if (!hadKey) {
           console.log("属性新增", key, value);
         } else if (hasChanged(value, oldValue)) {
           console.log("属性值被修改", key, value);
         }
         return result;
       };
     }
     export const mutableHandlers = {
       get, // 当获取属性时调用此方法
       set, // 当修改属性时调用此方法
     };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    1.4. Vue3.0 为什么要用 proxy?

    在 Vue2 中, 0bject.defineProperty 会改变原始数据,而 Proxy 是创建对象的虚拟表示,并提供 set 、get 和 deleteProperty 等处理器,这些处理器可在访问或修改原始对象上的属性时进行拦截,有以下特点∶

    • 不需用使用 Vue.$setVue.$delete 触发响应式。
    • 全方位的数组变化检测,消除了Vue2 无效的边界情况。
    • 支持 Map,Set,WeakMap 和 WeakSet。

    Proxy 实现的响应式原理与 Vue2的实现原理相同,实现方式大同小异∶

    • get 收集依赖
    • Set、delete 等触发依赖
    • 对于集合类型,就是对集合对象的方法做一层包装:原方法执行后执行依赖相关的收集或触发逻辑。
  • 相关阅读:
    can中继 智能CAN总线隔离中继器集线器CANBridge-300/400
    java微信小程序 ssm电动车智能充电系统
    学习笔记-Flutter Plugin开发流程
    Windows 下安装 Bun:像 Node 或 Deno 一样的现代 JavaScript 运行时
    转行软件测试有学历要求吗?低于大专是真的没出路吗?
    12.区块链系列之比特币NFT
    大数据docker部署
    【AI设计模式】01-数据表示-特征哈希(Feature Hashed)模式
    Uniapp有奖猜歌游戏系统源码,附带流量主
    Python爬虫怎么挣钱?解析Python爬虫赚钱方式,轻轻松松月入两万,再也不用为钱发愁啦
  • 原文地址:https://blog.csdn.net/weixin_46358949/article/details/126187434