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 问题
},