vue3 reactive实现

reactive 实现

需要考虑的场景

1.非对象类型处理
2.通过proxy代理,并解决 proxy的this指向失效问题
3.缓存代理过的对象,避免重复代理同一个普通对象
4.设置代理标识,避免代理已代理过的对象

实现结果

const enum ReactiveFlags {
    IS_REACTIVE = "__v_isReactive",
}
function reactive(target) {
    // 1.不对非对象类型进行处理
    if (val === null || typeof val !== "object") { // 相当于 !(value !== null && typeof value === "object")
        return target
    }
    // 4.如果代理已经代理过的对象,则返回已经代理过的对象,避免重复代理
    if (target[ReactiveFlags.IS_REACTIVE]) {
        return target
    }
    // 3.缓存代理过的对象,避免重复代理,下次代理时直接拿出来用
    const existsProxy = reactiveMap.get(target)
    if (existsProxy) {
        return existsProxy
    }
    // 2.代理,通过代理对象操作属性,回去源对象上获取
    const proxy = new Proxy(target, {
    	get(target, key, receiver) {// 目标   键名   当前代理对象
        	if (ReactiveFlags.IS_REACTIVE == key) {
            	// 第一次 target[ReactiveFlags.IS_REACTIVE]时,未触发getter,当代理已代理对象,会触发已代理的 getter
            	return true
        	}
        	return Reflect.get(target, key, receiver) // 处理了 this 问题
    	},
    	set(target, key, value, receiver) {// 目标   键名  键值  当前代理对象
        	return Reflect.set(target, key, value, receiver)
    	}
	})
    
    reactiveMap.set(target, proxy)
    return proxy
}

场景2

this指向失效 的问题

// 当使用proxy代理以下对象 person
let person = {
    name: 'jw',
    get aliasName() {
        return '**' + this.name + '**'
    }
}
let proxy = new Proxy(person,{
    get(target, key, receiver) {// 目标   键名   当前代理对象
        return target[key]
    },
    set(target, key, value, receiver) {// 目标   键名  键值  当前代理对象
        target[key] = value;
        return true
    }
})
console.log(proxy.aliasName) 
// proxy.aliasName 执行时 触发了proxy上的getter,target[key] 相当于 peron.aliasName
// peron.aliasName 执行时 触发了person上的getter操作,this.name 相当于 peron.name(this.name 中的 this 指向 person)
// this.name 执行时 并未触发 proxy 中的 getter  --- 会导致 this失效 的问题
打印结果  aliasName -> **jw**

解决方案: Reflect

let proxy = new Proxy(person,{
    get(target, key, receiver) {// 目标   键名   当前代理对象
        return Reflect.get(target,key,receiver)
    },
    set(target, key, value, receiver) {// 目标   键名  键值  当前代理对象
        return Reflect.set(target,key,value,receiver)
    }
})
console.log(proxy.aliasName)// 打印结果 aliasName -> name -> **jw**

场景3

缓存代理过的对象,避免重复代理

const obj = {a:1}
const p1 = reactive(obj);
const p1 = reactive(obj);

解决方案:weakMap 映射表

/*  let mapping = {
 *     target : proxy
*/  }
const existsProxy = reactiveMap.get(target)
if (existsProxy) {
   return existsProxy
}
...
reactiveMap.set(target, proxy)

场景4

设置代理标识,避免代理已代理过的对象

const state1 = reactive({a:1});
const state2 = reactive(state1);

解决方案:设置代理标识 __v_isReactive

if (target[ReactiveFlags.IS_REACTIVE]) {
    return target
}
...
get(target, key, receiver) {// 目标   键名   当前代理对象
    if (ReactiveFlags.IS_REACTIVE == key) {
        // 第一次 target[ReactiveFlags.IS_REACTIVE]时,未触发getter,当代理已代理对象,会触发已代理的 getter
        return true
    }
    return Reflect.get(target, key, receiver) // 处理了 this 问题
},
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柳晓黑胡椒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值