vue3 reactive实现

文章详细讨论了Reactive实现的几个关键场景,包括如何处理非对象类型,解决proxy的this指向问题,缓存已代理对象以避免重复代理,以及设置代理标识来防止多次代理同一对象。通过使用Reflect解决this指向失效问题,并利用WeakMap存储已代理对象,提高性能。

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 问题
},
### Vue 3reactive 的使用指南 在 Vue 3 中,`reactive` 是一个用于创建响应式对象的核心 API。它将普通 JavaScript 对象转换为响应式对象,允许开发者在组件中动态跟踪和更新数据[^1]。 #### 创建响应式对象 通过 `import { reactive } from &#39;vue&#39;;` 导入 `reactive` 函数后,可以将一个普通的对象传递给它以生成响应式版本。例如: ```javascript import { reactive } from &#39;vue&#39;; const state = reactive({ count: 0, message: &#39;Hello Vue!&#39; }); ``` 在这个例子中,`state` 是一个响应式对象,任何对 `state.count` 或 `state.message` 的更改都会触发视图的重新渲染[^1]。 #### 访问和修改响应式对象的值 访问和修改 `reactive` 创建的对象与访问普通对象的方式相同。例如: ```javascript console.log(state.count); // 输出:0 state.count++; console.log(state.count); // 输出:1 ``` 需要注意的是,`reactive` 只能用于对象类型(包括数组),不能直接作用于基本数据类型。如果需要创建基本类型的响应式引用,请使用 `ref` API[^1]。 #### 响应式对象的嵌套 `reactive` 自动处理嵌套对象的响应性。以下是一个包含嵌套对象的例子: ```javascript const nestedState = reactive({ user: { name: &#39;Alice&#39;, age: 25 } }); nestedState.user.name = &#39;Bob&#39;; console.log(nestedState.user.name); // 输出:Bob ``` #### 注意事项 - **不可变性**:`reactive` 返回的对象是原始对象的代理,因此不应尝试直接修改原始对象。 - **性能优化**:对于大型或复杂的数据结构,可以考虑使用 `shallowReactive` 来创建浅层响应式对象,从而减少性能开销[^1]。 #### 示例:在组件中使用 `reactive` 以下是一个完整的 Vue 3 组件示例,展示了如何在模板中绑定 `reactive` 对象的属性: ```vue <template> <div> <p>{{ state.message }}</p> <button @click="increment">Count: {{ state.count }}</button> </div> </template> <script> import { reactive } from &#39;vue&#39;; export default { setup() { const state = reactive({ count: 0, message: &#39;Hello Vue!&#39; }); const increment = () => { state.count++; }; return { state, increment }; } }; </script> ``` 在这个示例中,`state` 是一个响应式对象,其属性可以通过模板绑定动态更新。 #### 常见问题及解决方法 1. **无法检测到属性添加或删除**:Vue 3 的 `reactive` 使用 Proxy 实现响应式,能够检测到新增或删除的属性。如果遇到问题,请确保使用的是 Vue 3 版本。 2. **深层嵌套对象的响应性**:`reactive` 自动处理嵌套对象的响应性,但需注意不要混用 `reactive` 和 `ref`,以免导致不必要的复杂性[^1]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

柳晓黑胡椒

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

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

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

打赏作者

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

抵扣说明:

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

余额充值