引入ref的概念
由于Proxy拦截的都是非原始值,所以没有任何手段拦截对原始值的操作
let str = 'vue'
// 无法拦截对值的修改
str = 'vue3'
对于这个问题,我们能够想到的唯一办法是,使用一个非原始值去“包裹”原始值,例如一个对象
const wrapper = {
value: 'vue'
}
// 可以使用Proxy代理wrapper,就可以间接实现对原始值的拦截
const name = reactive(wrapper)
name.value // vue
// 修改值可以触发响应式
name.value = "vue3"
为此,我们可以封装一个函数
function ref(val) {
const wapper = {
value: val
}
// 将对象变成响应式数据
return reactive(wrapper)
}
// 创建原始值的响应式数据
const refVal = ref(1)
effect(() => {
// 读取value
console.log(refVal.value)
})
refVal.value = 2
一个小问题
如何区分refVal是原始值的包裹对象,还是非原始值的响应式数据呢
function ref(val) {
const wapper = {
value: val
}
Object.defineProperty(wapper, '__v_isRef', {
value: true
})
return reactive(wrapper)
}
响应丢失问题
在setup的return中,返回
return {...obj}
然后在组件中这样使用:
// 假设obj有foo属性,值为1
<p>{{foo}}</p>
会使obj的foo属性修改时响应式丢失,因为上述return 相当于
return {foo: 1}
需要封装一个函数,
function toRef(obj, key) {
const wapper = {
get value() {
return obj[key]
},
set value(val) {
obj[key] = val
}
}
return wapper
}
每个属性都单独加响应式太多了,再封装一个
function toRefs(obj, key) {
const ret = {}
for(const key in obj) {
ret[key]= toRef(obj, key)
}
return ret
}
自动脱Ref
自动脱Ref,就是指属性的访问行为,如果读取的属性是一个ref,不用.value, 减轻用户心智负担
function proxyRefs(target) {
return new Proxy(target, {
get(target, key, receiver) {
const value = Reflect.get(target, key, receiver)
// 如果读取的值是ref,则返回它的value
return value.__v_isRef ? value.value : value
},
set(target, key, newValue,receiver) {
const value = target[key]
if(value.__v_idRef) {
value.value = newValue
return true
}
}
return Reflect.set(target, key, newValue, receiver)
})
}
const newObj = proxyRefs({ ...toRefs(obj) })