1.引入ref的概念
Proxy只能代理非原始值,如果要拦截原始值,我们需要用一个非原始值去包裹原始值:
const wrapper = {
value:"vue"
}
const name = reactive(wrapper);
而包裹原始值的对象不应当由用户创建,我们可以封装函数:
funtion ref(val){
const wrapper = {
value:val
}
return reactive(wrapper)
}
然而我们无法区分一个值到底是原始值的包裹对象还是非原始值的响应数据:
const refVal1 = ref(1),refVal2 = reactive({value:2)};
上述的refVal1和refVal2没有区别。为了区分,我们为ref的包裹对象定义__v_isRef属性:
funtion ref(val){
const wrapper = {
value:val
}
Object.defineProperty(wrapper,"__v_isRef",{
value:true
})
return reactive(wrapper)
}
2.响应丢失问题
在编写Vue组件时,我们要把数据暴露到模板中:
export default {
setup(){
const obj = reactive({foo:1,bar:2});
return {...obj}
}
}
然后在模板中访问数据
<template>
<p>{{foo}}</p>
</template>
然而展开运算符...返回的是普通对象,{...obj}等价于{foo:1,bar:2},它不具备响应式能力。为了解决这个问题,我们1要定义一个新对象,当访问它的值时实际上会返回obj的值。
funtion toRef(obj,key){
const wrapper = {
get value(){
return obj[key]
}
set value(val){
obj[key] = val;
}
}
return wrapper
}
funtion toRefs(obj){
const ret = {};
for(const key in obj){
ret[key] = toRef(obj,key)
}
return ret
}
const newObj ={...toRefs(obj)}
这样访问newObj的值就可以得到obj的值,也会触发响应。
3.自动脱ref
由于toRefs会把响应式数据的第一层转化为ref,所以必须用value属性访问值,这样很不方便,此时要用到__v_isRef标识:
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;
}
})
}
而setup函数中返回的数据会由proxyRefs函数处理。