VUE3响应式原理,及与VUE2响应式的区别

本文详细解析了Vue3中响应式原理,基于Proxy和Reflect实现,介绍了Proxy的常用拦截方法,以及与Vue2响应式(object.defineProperty)的区别,涉及数据劫持、数组操作和性能优化等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

响应式原理和vue响应原理的相关概念,在之前的博客中已经说明vue3的响应式实现是基于ES6 中的 proxyReflect实现的,与vue2借助object.defineProperty有所不同。要想理解vue3的响应式实现,必须要先了解proxyReflect的作用。

Proxy(代理模式)

Proxy 译为代理,用于修改某些操作的默认行为,就形同于在编程语言层面上做修改,属于元编程。可简单理解为在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,进而能实现对外界的访问进行过滤和改写。

语法

Proxy构造函数:var proxy = new Proxy(target, handler);

  • target: 需要拦截的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理),但target 不能是原始值(如字符串、数字、布尔值、null 或 undefined)或不可遍历的数据结构(如普通变量)。
  • handler: 用来定制拦截行为。

常用的拦截方法

方法描述
get(target, propKey, receiver)拦截对象属性的读取
set(target, propKey, value, receiver)拦截对象属性的设置
has(target, propKey)拦截propKey in proxy的操作,返回一个布尔值。
deleteProperty(target, propKey)拦截对象属性删除操作
ownKeys(target)拦截Object.getOwnPropertyNames(proxy)Object.getOwnPropertySymbols(proxy)Object.keys(proxy)for...in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果包括目标对象自身的可遍历属性。
apply(target, object, args)拦截 Proxy 实例作为函数调用的操作。比如proxy(...args)proxy.call(object, ...args)proxy.apply(...)
construct(target, args)拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)

参数说明:

  • target: 操作的目标对象
  • propkey: 操作目标对象的关键字
  • receiver: 当前的Proxy对象或者继承的Proxy对象

Proxy的this问题

Proxy可以代理针对目标对象的访问,但它不是目标对象的透明代理。简单理解就是在 Proxy 代理的情况下,目标对象内部的this关键字会指向 Proxy 代理。,下面是个简单的例子:

const _name = new WeakMap();
class Person {
  constructor(name) {
    _name.set(this, name);
  }
  get name() {
    return _name.get(this);
  }
}
const jane = new Person('Jane');
jane.name // 'Jane'
const proxy = new Proxy(jane, {});
proxy.name // undefined 由于通过proxy.name访问时,this指向proxy,导致无法取到值

同理,有些原生对象的内部属性,Proxy 也无法代理这些原生对象的属性。

const target = new Date();
const proxy = new Proxy(target, {});
proxy.getDate();// TypeError: this is not a Date object.

Reflect

Reflect对象与Proxy对象一样,也是 ES6 为了操作对象而提供的新 APIReflect 对象的出现使得在 JavaScript 中进行元编程变得更加方便和一致。其设计的目的归结于一下几点:

  • **为了更好地分离语言内部的方法和普通的对象操作方法:**将Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上,从而
  • **提供了一组操作对象时的标准行为:**修改某些Object方法的返回结果,让其变得更合理。例如Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)则会返回false
  • 提供了一组与操作符相对应的方法:Object操作都变成函数行为。例如 Reflect.get()Reflect.set()Reflect.has() 等,这些方法与对应的操作符(如 .[]in 等)具有相似的功能。
  • Proxy对象可以方便地调用对应的Reflect方法: Reflect对象的方法与Proxy对象的方法一一对应,只要是Proxy对象的方法,就能在Reflect对象上找到对应的方法。总之,不管Proxy怎么修改默认行为,你总可以在Reflect上获取默认行为。

总的来说,Reflect 对象的设计目的是为了提供一组操作对象的方法,使得在 JavaScript 中进行元编程更加方便、一致和可靠,得开发者可以更加灵活地操作对象和修改程序的行为。

VUE3 数据劫持的简单复现

 // 监听数据 reactive(引用数据类型) ref(基础数据类型)
function isObject(obj) {
    return typeof obj === 'object' && obj !== null;
}
function reactive(obj) {
    return new Proxy(obj, {
        get(target, key, receiver) {
            console.log("获取" + key + "值")
            let res = Reflect.get(target, key, receiver)
            // 递归调用 
            return isObject(res) ? reactive(res) : res 
        },
        set(target, key, receiver) {
            console.log("设置" + key + "值")
            return Reflect.set(target, key, receiver)
        },
        deleteProperty(target, key) {
            console.log("删除" + key + "值")
            return Reflect.deleteProperty(target, key)
        }
    })
}
let test = reactive({name: '张三', request: { code: '200' }})
test.name = "李四"
console.log(test.request.code)
test.addKey = 'test'
delete test.addKey
console.log("数组对象操作")
let arr = reactive([1,2,3])
arr.push(4)

在这里插入图片描述

VUE2 与 VUE3 响应式的区别

VUE2 响应式(object.defineProperty)的缺陷

  • 对对象的添加和删除操作,无法劫持 (解决方法:新增 set 和 delete)
  • 对数组的api无法劫持到 (解决方法:重写数组的api
  • 存在深层嵌套关系,性能问题(无脑递归)

VUE3响应式

  • 解决了Vue2中无法追踪数据新增或删除属性的问题
  • 可以直接监听数组,无需想Vue2响应式那样需要重写数组拦截方式

在这里插入图片描述

在这里插入图片描述

vue2响应式的实现请看之前的博客

Proxy详解:https://www.bookstack.cn/read/es6-3rd/spilt.1.docs-proxy.md
Reflect详解:https://www.bookstack.cn/read/es6-3rd/docs-reflect.md

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值