《Vue进阶教程》第三十五课:自动脱ref
往期内容:
《Vue进阶教程》第二十四课:优化
《Vue进阶教程》第二十五课:watch基本概念
《Vue进阶教程》第二十六课:实现侦听函数
《Vue进阶教程》第二十七课:实现侦听对象
《Vue进阶教程》第二十八课:实现新旧值
《Vue进阶教程》第二十九课:立即执行的回调
《Vue进阶教程》第三十课:watchEffect
《Vue进阶教程》第三十一课:ref的初步实现
《Vue进阶教程》第三十二课:ref响应丢失问题
《Vue进阶教程》第三十三课:toRef的使用
《Vue进阶教程》第三十四课:toRefs的使用
1) 什么是自动脱ref
所谓自动脱ref, 就是不写.value
对于ref
类型数据, 每次在访问时, 需要加.value
才能触发响应式.
但是这样做无疑增加了心智负担, 尤其是在写模板时, 不够优雅
为此, Vue3提供一个API: proxyRefs
对传入的ref
类型对象进行代理, 返回proxy
对象
个人理解: 有点类似toRefs的逆操作??
2) 基本使用
使用演示
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="./vue.js"></script>
</head>
<body>
<script>
const { ref, proxyRefs, effect } = Vue
const count = ref(0)
// 1.模拟setup的返回对象
// setup函数返回的对象会经过proxyRefs处理
// 这样在模板中就不用写.value了
const setup = proxyRefs({
count,
})
// 2.模拟页面渲染
effect(() => {
console.log('不用通过.value访问', setup.count)
})
</script>
</body>
</html>
3) proxyRefs的实现
基本实现
function proxyRefs(objectWithRefs) {
return new Proxy(objectWithRefs, {
get(target, key, receiver) {
// 使用Reflect读取target[key]
const obj = Reflect.get(target, key, receiver)
// 如果obj是ref类型, 返回obj.value; 否则, 直接返回
return obj.__v_isRef ? obj.value : obj
},
set(target, key, newVal, receiver) {
const obj = target[key]
if (obj.__v_isRef) {
obj.value = newVal
return obj
}
return Reflect.set(target, key, newVal, receiver)
},
})
}
源码解读
源码对传入参数加强了判断
- 如果objectWithRefs已经是
reactive
类型, 就直接使用
源码按功能进一步细化, 可读性更高
unref
函数可以复用: 如果是ref
返回.value
; 否则直接返回- 将proxy的handler提取成
shallowUnwrapHandlers
函数 - 在set时, 加入了新旧值类型的判断, 更严谨
function unref(ref) {
return isRef(ref) ? ref.value : ref
}
const shallowUnwrapHandlers = {
get: (target, key, receiver) => unref(Reflect.get(target, key, receiver)),
set: (target, key, value, receiver) => {
const oldValue = target[key]
if (isRef(oldValue) && !isRef(value)) {
oldValue.value = value
return true
} else {
return Reflect.set(target, key, value, receiver)
}
},
}
function proxyRefs(objectWithRefs) {
return isReactive(objectWithRefs)
? objectWithRefs
: new Proxy(objectWithRefs, shallowUnwrapHandlers)
}