6.3 自动脱 ref
proxyRefs 用 Proxy 拦截读写:读到 ref 就直接返回它的
value,赋值就直接改它的value,从而省掉.value。
一、为什么需要自动脱 ref
toRefs会把响应式数据第一层的属性值都转换成 ref,于是每次取值都得写.value:newObj.foo.value。- 而用户通常是在模板里访问数据的,多写一层
.value是额外的心智负担。 - 自动脱 ref:读取属性时,如果它是一个 ref,就直接返回该 ref 的
value,即newObj.foo等价于newObj.foo.value。
二、proxyRefs 实现
function proxyRefs(target) {
return new Proxy(target, {
get(target, key, receiver) {
const value = Reflect.get(target, key, receiver)
// 自动脱 ref:读到 ref 就返回它的 value
return value.__v_isRef ? value.value : value
},
set(target, key, newValue, receiver) {
const value = target[key]
// 原本是 ref,就改它的 value(而不是把整个 ref 替换掉)
if (value.__v_isRef) {
value.value = newValue
return true
}
return Reflect.set(target, key, newValue, receiver)
}
})
}
使用:
const obj = reactive({ foo: 1, bar: 2 })
const newObj = proxyRefs({ ...toRefs(obj) })
effect(() => {
console.log(newObj.foo) // 不用写 .value
})
newObj.foo = 100 // 自动写入 newObj.foo 这个 ref 的 value 属性
get负责脱 ref:依赖 6.1 引入的__v_isRef标识判断是不是 ref。set负责写回:如果原属性是 ref 就只改它的value,保持 ref 结构不被替换;否则按普通属性Reflect.set。
三、reactive 也有自动脱 ref 的能力
const count = ref(0)
const obj = reactive({ count })
obj.count // 0,而不是 ref 对象 —— reactive 返回的代理对象同样会脱 ref
四、模板里为什么不需要写 .value
- 组件从
setup返回的对象,会被内部用proxyRefs包裹一层再交给模板渲染。 - 于是模板中访问
count时,get已经自动脱 ref,写{{ count }}即可,无需{{ count.value }}。 - 而在普通 JS 逻辑代码中没有这层代理,
ref必须显式写.value。
记忆:
ref是对象,.value是属性访问;只有在被proxyRefs包裹的上下文(setup返回值 → 模板、reactive包裹 ref)里,这层.value才会被自动省略。
相关
- 6.2 响应丢失问题:toRef / toRefs 把属性转成 ref,是自动脱 ref 的前提