Vue3响应式方案

非原始值的响应式方案

先看下面实例代码

const obj = {
    foo: 1,
    get bar() {
        return this.foo
    }
}

const p = new Proxy(obj, {
    get(target, key) {
        track(target, key);
        return target[key]
    },
    set(target, key, newValue) { 
        target[key] = newValue
        trigger(target, key);
    }
})

effect(() => {
    console.log(p.bar) // 1
})

p.foo++;

执行p.foo++后,副作用函数并没有重新执行。原因是:

当effect副作用函数执行时,会读取p.bar,p.bar是一个访问器属性,因此执行getter函数,getter函数中返回了this.foo。此时的this指向的是谁呢?

回顾一下整个流程:首先通过代理对象p访问p.bar,这回触发代理对象p的get拦截函数,拦截函数返回target[key],target是原始对象obj,key是bar,所以target[key]相当于obj.bar。又因为obj.bar是访问器属性,他的this指向的是原始对象obj。这说明最终访问的其实是obj.foo,等价于

effect(() => {
    obj.foo
})

在副作用函数内,通过原始对象访问他的某个属性是不会建立响应联系的

那个整个问题应该如何解决呢?

Reflect

const p = new Proxy(obj, {
    get(target, key, receiver){
        track(traget, key)
        return Reflect.get(target, key, receiver)
    }
})

代理对象get拦截函数接收的第三个参数receiver,代表了谁在调取属性。

p.bar // 代理对象p在读取bar属性

const obj = {
    foo: 1,
    get bar() {
        // 现在这里的this,指向的就是代理对象p
        return this.foo
    }
}

Refleft详情

Proxy工作原理

根据ECMAscript规范,js中有两种对象:常规对象异质对象

在js中函数也是对象,给出一个obj,如何区分他是普通对象还是函数?在js中,对象的实际语义是由对象的内部方法指定的。所谓内部方法,指的是当我们对一个对象进行操作时在引擎内部调用的方法,这些方法对js使用者不可见。

obj.foo // 引擎内部调用[[Get]]整个内部方法来读取属性。

js的内部方法 11 + 2个必要方法

由此可知,如何区分普通对象和函数:如果对象是一个函数,那个这个对象必须部署内部方法[[Call]],但普通函数不会。

常规对象:

  • 满足必要的内部方法,且必须使用ECMA规范给出定义实现
  • 对于内部方法[[Call]] [[Construct]]必须使用ECMA规范定义实现

不满足以上条件的均为异质对象。

Proxy就是异质对象,Proxy内部[[Get]]没使用标准定义实现。Proxy中如果没有指定get()拦截函数,那就通过原始[[Get]]方法获取值。

Proxy:创建代理对象时指定了get拦截函数,用来自定义代理对象本身的内部方法和行为。

除了get以外,还有[[Set]] [[Delete]] [[GetPrototypeOf]] [[SetPrototypeOf]] [[HasProperty]]。。。等多个内部方法。

// 代理内部删除属性
const p = new Proxy(obj, {
    deleteProperty(target, key){
        return Reflect.deleteProperty(target, key)
    }
})

原始值响应方案ref

原始值是按值传递的,而非按引用传递。一个函数接受原始值作为参数,形参和实参之间没有引用关系。

针对这个问题,对原始值做一层包裹,变成响应式数据

function ref(val){
    const wrapper = {
        value: val
    }
    return reactive(wrapper)
}

总结

Vue.js3的响应式数据是基于Proxy实现的,Proxy可以为其他对象创建一个代理对象。所谓代理,指的是对一个对象的基本语义的代理。它允许我们拦截重新定义对一个对象的基本操作。在实现代理的过程中,会遇到访问器属性this的指向问题,通过使用Reflect.* 方法并指定正确的receiver来解决。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值