likes
comments
collection
share

你一定看得懂的Vue3响应式实现原理

作者站长头像
站长
· 阅读数 5

浅聊一下

Proxy

我们先来看看阮一峰老师对于Proxy的描述

Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。

可能不是那么通俗易懂,我们上代码

<!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>
 <script>
    let person = {
        name: '张三',
        age: 18,
        sex:'男'
    }
    let p = new Proxy(person,{})
 </script>
</body>
</html>

Proxy需要接收两个参数const p = new Proxy(target, handler)

  • target

    要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。

  • handler

    一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为。

我们在上述例子中传入了一个person对象,另一个参数操作暂不填写,在这里p就是我们的代理对象,当我们要对person进行增删改查的时候,p就会拦截下来,然后进行handler中的操作...

我们在控制台进行如下操作:

你一定看得懂的Vue3响应式实现原理

可以看到当我们对p的值进行操作的时候,person对象的值也会改变,那么我们的响应式实现原理就到此结束了吗?还没有!我们只是通过代理修改了person的内容而已,要实现响应式,还得对修改进行捕获...

响应式实现

在Proxy中我们提到,接收的第二个参数Handler对象中存储的是要进行的各种代理操作,那么我们将在里面对修改进行一个捕获...

    let p = new Proxy(person,{
        get(target,prop){
            console.log(`读取了p上的${prop}属性`,target,prop);
            return target[prop]
        },
        set(target,prop,value){
            console.log(`修改了p上的${prop}属性`,target,prop,value);
            target[prop] = value
        },
        deleteProperty(target,prop){
            console.log(`删除了p上的${prop}属性`,target,prop);
            return delete target[prop]
        }
    })
  1. get

    • 当你尝试获取代理对象 p 上的属性时(例如 p.name),这个拦截器会被调用。
    • 它接收两个参数:target 表示目标对象(这里是 person),prop 表示要获取的属性名(例如 "name")。
    • 这个拦截器会输出一条日志,说明你在读取代理对象 p 上的哪个属性,并返回目标对象上相应属性的值。
  2. set

    • 当你尝试设置代理对象 p 上的属性时(例如 p.age = 35),这个拦截器会被调用。
    • 它接收三个参数:target 表示目标对象(这里是 person),prop 表示要设置的属性名(例如 "age"),value 表示要设置的值(例如 35)。
    • 这个拦截器会输出一条日志,说明你在修改代理对象 p 上的哪个属性,并在目标对象上设置相应的属性值。
  3. deleteProperty

    • 当你尝试删除代理对象 p 上的属性时(例如 delete p.age),这个拦截器会被调用。
    • 它接收两个参数:target 表示目标对象(这里是 person),prop 表示要删除的属性名(例如 "age")。
    • 这个拦截器会输出一条日志,说明你在删除代理对象 p 上的哪个属性,并在目标对象上删除相应的属性。

来看看效果:

你一定看得懂的Vue3响应式实现原理

将增删改查全部调用一遍,发现捕获了操作并且实现了响应式

但是,Vue3响应式的底层这么写有一点low了,vue3使用到了Reflect

 let p = new Proxy(person,{
        get(target,prop){
            console.log(`读取了p上的${prop}属性`,target,prop);
            return Reflect.get(target,prop)
        },
        set(target,prop,value){
            console.log(`修改了p上的${prop}属性`,target,prop,value);
            return Reflect.set(target,prop,value)
        },
        deleteProperty(target,prop){
            console.log(`删除了p上的${prop}属性`,target,prop);
            return Reflect.deleteProperty(target,prop)
        }
    })

在真正的响应式原理中,通常会使用 Reflect 对象来操作目标对象,而不是直接操作目标对象本身。这样做有几个优点:

  1. 更加规范和易读: 使用 Reflect 提供的方法,如 Reflect.getReflect.setReflect.deleteProperty,使代码更符合规范,易于阅读和理解。这些方法的名称清晰地表明了它们的功能,使代码更易于维护和修改。
  2. 更安全: Reflect 方法的使用会使代码更安全,因为它们提供了一种标准的方式来执行基本操作,避免了直接操作目标对象可能导致的一些潜在问题。例如,Reflect.set 方法会返回一个布尔值,指示属性是否成功设置,而直接设置属性时,你必须自己处理设置是否成功的情况。
  3. 拓展性: 使用 Reflect 提供的方法,使得代码更具有拓展性。你可以在这些方法的基础上自定义更复杂的行为,而不会影响到 Proxy 的行为。
  4. 一致性: Reflect 方法的行为与语言内部操作的行为一致,这使得代码更一致和可预测。与直接操作目标对象不同,Reflect 方法在处理特殊情况时也会返回一致的结果。

结尾

说到这里,vue3响应式实现原理你就应该会了...