Vue源码系列2:模拟实现vue3.x中的reactive

前言

相比于vue2.x的Object.defineProperty,vue3.x中的reactive中主要是利用Proxy去实现,这里对其进行模拟实现。

一. 完整代码

const reactMap = new WeakMap();
const ReactiveFlags = {
    IS_REACTIVE : "isReactive"
}
// 将数据转化成响应式的数据,只能做对象的代理
function reactive(target) {
    if(!(typeof target === 'object' && target !== null)) {
        return;
    }
    if(target[ReactiveFlags.IS_REACTIVE]) {
        return target
    }
    let exisProxy = reactMap.get(target);
    if(exisProxy) {
        return exisProxy
    }
    const proxy = new Proxy(target,{
        get(target,key,receiver) {
        	if(key === ReactiveFlags.IS_REACTIVE) {
        		return true
        	}
            return Reflect.get(target,key,receiver)
        },
        set(target,key,value,receiver) {
            return Reflect.set(target,key,value,receiver)
        }
    });
    reactMap.set(target,proxy)
    return proxy
}

一. 为什么用Reflect.get和Reflect.set?

除了语义化更强些外,其实也更好地兼容了taget里面有get和set的情况
比如

const obj = {
	name: 'sandy',
	get formatValue() {
	  return `format${this.name}`
	}
}

如果用target[key]的方式访问的话,get只会执行一次,即只能监控到key为formatValue的情况,但是因为formatValue依赖于name属性,所以当访问formatValue属性的同时,get应该执行2次,key为formatValue和name。而该功能Reflect.get正好可以满足。

Reflect.set类似于Reflect.get。

二. 处理相同target的问题

测试代码

const target = {name: 'sandy'}
const t1 = reactive(target);
const t2 = reactive(target);

核心代码

const reactMap = new WeakMap();
 let exisProxy = reactMap.get(target);
    if(exisProxy) {
        return exisProxy
    }

 reactMap.set(target,proxy)

如果删掉上面的核心代码,则t1和t2会分别创建一个proxy,因为是同一个target,所以可以节省一次创建proxy的过程;

可以用weakMap,对target进行标识,如果已经代理过的话,则直接返回原来代理的值;

使用weakMap的好处是不会造成内存泄漏,但是要注意weakMap的key只能为对象

三. 处理target为之前代理过的proxy的情况

比如

const target = {name: 'sandy'}
const t1 = reactive(target);
const t2 = reactive(t1);

像这种情况,t2返回的就是t1,所以我们也可对此优化

核心代码

const ReactiveFlags = {
    IS_REACTIVE : "isReactive"
}
if(target[ReactiveFlags.IS_REACTIVE]) {
        return target
}

if(key === ReactiveFlags.IS_REACTIVE) {
       return true
}

实现逻辑:

  1. 第一次reactive(target);正常实现一次代理逻辑;
  2. 第二次reactive(t1),因为此时的target为t1,之前走过一次代理了,所以当target[ReactiveFlags.IS_REACTIVE]执行时会访问get中的
if(key === ReactiveFlags.IS_REACTIVE) {
       return true
}

返回结果为true之后,所以直接返回了target

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值