2025.09.26
Vue3 响应式工具:toRef、toRefs、unRef、proxyRef
在使用 Vue 3 的 reactive 时,我们经常习惯性的使用 ES6 解构赋值。
在使用 Vue 3 的 reactive 时,我们经常习惯性的使用 ES6 解构赋值。
const state = reactive({
name: '张三',
age: 18
})
let { name } = state
effect(() => {
console.log(name) // 仅仅是一个普通的字符串 "张三"
})
// 1秒后修改,effect 不会重新触发
setTimeout(() => {
name = '李四'
}, 1000)
这样的话会发现丢失了响应式, 💡
reactive返回的是一个 proxy 对象,而结构出来的name只是一个基础类型, 失去了与原对象的代理联系
1.toRef
为了解决这个问题, 通常的情况下会使用 toRef, 让结构出来的变量可以触发响应式更新
const state = reactive({
name: '张三',
age: 18
})
const name = toRef(state, 'name')
console.log(name)
effect(() => {
console.log(name.value) })
setTimeout(() => {
state.name = '李四'}, 1000)
这时候输出的 name 的类型

可以看到和 RefImpl 的类型不同, 它是一个特质的 ObjectRefImpl 类, 并且多了属性 _object 和 _key,这两个属性分别指向了
state 和 name
核心实现
这个 toRef 从使用上可以看到接受一个对象和 key 所以⬇️
function toRef(object: object, key: string | symbol) {
return {
get value() {
return object[key]
},
set value(newValue) {
object[key] = newValue
}
}
}
这样的话其实就可以更新了, 但是官方的实现是一个类,所以修改一下
// ref.ts
class ObjectRefImpl {
[ReactiveFlags.IS_REF] = true
constructor(
public _object,
public _key
) {
}
get value() {
return this._object[this._key]
}
set value(newValue) {
this._object[this._key] = newValue
}
}
export function toRef(target: object, key: string | symbol) {
return new ObjectRefImpl(target, key)
}
2. toRefs
const state = reactive({ name: '张三', age: 18 })
const { name, age } = toRefs(state)
setTimeout(() => {
name.value = '李四'
}, 1000)
有了 toRef,toRefs 不就轻松实现嘛
既然 toRef 可以将对象的一个属性转为响应式的引用, 那 toRefs的不就能轻松实现嘛
遍历所有属性, 调用
toRef
export function toRefs(object) {
const ret = {}
for (const key in object) {
ret[key] = toRef(object, key)
}
return ret
}
这样的话就导出都是 .value了,每次 .value 也挺烦的
这样的话就引出来两个辅助方法了
3. unRef、proxyRef
什么是 proxyRef 尼
为什么在
template中使用ref不需要.value这其实就是proxyRef的作用
当访问这个代理的属性时,会自动解包.value 和 reactive 很像, 不直接使用 reactive的原因是 reactive 是深层响应式的,
proxyRef 是浅层的
export function proxyRefs(target) {
return new Proxy(target, {
get(target, key, receiver) {
const res = Reflect.get(target, key, receiver)
// return isRef(res) ? res.value : res return unRef(value) },
set(target, key, newValue, receiver) {
const oldValue = target[key]
if (isRef(oldValue) && !isRef(newValue)) {
oldValue.value = newValue
return true
}
return Reflect.set(target, key, newValue, receiver)
}
})
}
export function unRef(value) {
return isRef(value) ? value.value : value
}