实现 reactiveRef 的方法
以下是实现 reactiveRef 的方法,满足你的需求:
import { ref, isRef } from 'vue';
function reactiveRef(initialValue) {
const internalRef = ref(initialValue);
return new Proxy(internalRef, {
get(target, prop, receiver) {
// 直接访问实例时返回 .value 的值
if (prop === Symbol.toPrimitive || prop === 'valueOf') {
return () => target.value;
}
if (prop === 'toString') {
return () => target.value.toString();
}
// 访问 .value 属性时返回原始值
if (prop === 'value') {
return target.value;
}
// 如果当前内容是对象,则代理到内部
const v = internalRef.value;
if (v && typeof v === 'object') return v[prop]
// 保留 ref 的其他属性(如 .effect)
return Reflect.get(target, prop, receiver);
},
set(target, prop, value, receiver) {
// 设置 .value 属性时修改原始值
if (prop === 'value') {
target.value = value;
return true;
}
// 否则给内部对象赋值
if (typeof internalRef.value === 'object' && internalRef.value !== null) {
internalRef.value[prop] = value;
return true
}
// 其他属性直接设置
return Reflect.set(target, prop, value, receiver);
},
// 直接调用对象(如reactiveRef(1)())时返回internalRef.value
apply(target, thisArg, argumentsList) {
return internalRef.value
}
});
}
// 使用示例
const count = reactiveRef(0)
console.log(count.value) // 0
count.value = 10;
console.log(count.value) // 10
// 如果value默认是一个对象
const state = reactiveRef({a: 1, b: 2});
console.log(state.a) // 1
state.a = 12
console.log(state.a) // 12
// 依然可以用state.value = {...}替换整个对象
state.value = {a: 9, b: 9}
console.log(state.a)
console.log(state.value.b)
特性说明
- 直接访问值:当直接访问
count时,会自动返回.value的值 - 强制使用
.value设置:必须通过count.value = newValue修改值 - 保留完整功能:
- 仍可通过
count.value访问原始ref对象 - 保留
isRef检测等原始功能 - 响应式系统正常工作
- 仍可通过
注意事项
- 不要直接给
count变量赋值(如count = 5),这会改变变量指向的对象 - 深层次对象仍需保持响应式(与普通
ref行为一致) - 在模板中使用时仍会自动解包,与普通
ref行为一致
这个实现通过 Proxy 拦截了属性的访问和设置,在保持 Vue 响应式系统完整性的同时,修改了值的访问方式。
---------------文章补充
- 用
reactiveRef创建响应式变量后,访问属性和赋值都不需要.value,直接用变量名即可操作属性。 - 但在需要整体替换变量内容时,可以通过
.value赋新值。
这类似于把 ref 和 reactive 的优点结合起来。你可以自定义如下的 reactiveRef 实现:
import { reactive, ref } from 'vue'
export function reactiveRef(initialValue) {
const _ref = ref(initialValue)
const proxy = reactive({
get value() {
return _ref.value
},
set value(val) {
_ref.value = val
// 如果你希望自动把新对象变成响应式,可以加下面这句
// Object.assign(proxy, val)
}
})
// 代理内部属性访问,实现不用.value即可访问
return new Proxy(proxy, {
get(target, key, receiver) {
if (key === 'value') return target.value
// 允许直接访问和设置属性
return Reflect.get(target.value, key, receiver)
},
set(target, key, value, receiver) {
if (key === 'value') {
target.value = value
return true
}
// 允许直接设置属性
target.value[key] = value
return true
},
ownKeys(target) {
return Reflect.ownKeys(target.value)
},
getOwnPropertyDescriptor(target, key) {
return Object.getOwnPropertyDescriptor(target.value, key)
}
})
}
用法示例
const state = reactiveRef({ name: 'Alice', age: 20 })
// 直接访问属性,无需 .value
console.log(state.name) // 'Alice'
state.age = 21
console.log(state.age) // 21
// 整体赋值用 .value
state.value = { name: 'Bob', age: 30 }
console.log(state.name) // 'Bob'
特点说明
- 你可以像
reactive一样直接用state.xxx访问和修改属性。 - 你可以像
ref一样用state.value赋值来整体替换对象。 - 这样在模板和脚本中都很方便,减少了 .value 的繁琐操作。
下面对代码进行详细解释。这个 reactiveRef 函数的目的是结合 ref 和 reactive 的优点,实现如下效果:
- 像操作
reactive一样访问属性(不用.value)。 - 像操作
ref一样可以通过.value整体赋值。
代码逐行详解
import { reactive, ref } from 'vue'
引入 Vue 3 的响应式核心 API:reactive(对象响应式)和 ref(引用响应式)。
export function reactiveRef(initialValue) {
定义一个名为 reactiveRef 的函数,接收初始值参数 initialValue。
const _ref = ref(initialValue)
用 ref 包裹初始值,生成一个响应式引用 _ref。_ref.value 就是当前的真实数据。
const proxy = reactive({
get value() {
return _ref.value
},
set value(val) {
_ref.value = val
// 如果你希望自动把新对象变成响应式,可以加下面这句
// Object.assign(proxy, val)
}
})
- 定义一个响应式对象
proxy,它有一个value属性。 get value()返回_ref.value,即当前值。set value(val)可以整体替换_ref.value,实现 ref 的“整体赋值”效果。- 注释提到如果你想让新对象的属性也自动变成响应式,可以用
Object.assign(proxy, val),但这通常不是必需的。
// 代理内部属性访问,实现不用.value即可访问
return new Proxy(proxy, {
get(target, key, receiver) {
if (key === 'value') return target.value
// 允许直接访问和设置属性
return Reflect.get(target.value, key, receiver)
},
set(target, key, value, receiver) {
if (key === 'value') {
target.value = value
return true
}
// 允许直接设置属性
target.value[key] = value
return true
},
ownKeys(target) {
return Reflect.ownKeys(target.value)
},
getOwnPropertyDescriptor(target, key) {
return Object.getOwnPropertyDescriptor(target.value, key)
}
})
}
这里是重点:
- 使用
Proxy代理proxy对象。 get:- 如果访问的是
value属性,返回proxy.value,就是_ref.value。 - 如果访问的是其它属性(比如
state.name),实际上是取_ref.value.name。
- 如果访问的是
set:- 如果设置的是
value属性,就是整体赋值(state.value = { ... })。 - 否则,就是给
_ref.value的属性赋值(state.name = 'xxx')。
- 如果设置的是
ownKeys和getOwnPropertyDescriptor:- 让你可以像普通对象一样遍历和获取属性描述符,比如
Object.keys(state)、for...in等都能正常用。
- 让你可以像普通对象一样遍历和获取属性描述符,比如
总结
这个函数的作用:
- 你可以用
state = reactiveRef({ ... })创建响应式对象。 state.xxx直接访问和设置属性,不用.value。state.value = { ... }可以整体替换对象,像 ref 一样。- 本质上是一个“带整体赋值功能的 reactive 对象”。
使用示例
const state = reactiveRef({ name: 'Alice', age: 20 })
console.log(state.name) // 'Alice'
state.age = 22 // 响应式修改属性
state.value = { name: 'Bob', age: 30 } // 整体替换
console.log(state.name) // 'Bob'
如果你想了解 Proxy 的细节,或者响应式的原理,请随时追问!