原始值的响应式实现
-
Proxy 无法提供对原始值的代理需要使用一层对象作为包裹,间接实现原始值的响应式方案。由于“包裹对象”本质上与普通对象没有任何区别,因此为了区分 ref 与普通响应式对象,我们还为“包裹对象”定义了一个值为 true 的属性生,即 __v_isRef,用它作为 ref 的标识。
-
ref 除了能够用于原始值的响应式方案之外,还能用来解决响应丢失问题。为了解决该问题,我们实现了 toRef 以及 toRefs 这两个函数。它们本质上是对响应式数据做了一层包装,或者叫作“访问代理”。
function ref(val) {
const wrapper = {
value: val,
};
Object.defineProperty(wrapper, "__v_isRef", {
value: true,
});
return reactive(wrapper);
}
const obj = reactive({ foo: 1, bar: 2 });
function toRefs(obj) {
const ret = {};
for (const key in obj) {
ret[key] = toRef(obj, key);
}
return ret;
}
function toRef(obj, key) {
const wrapper = {
get value() {
return obj[key];
},
set value(val) {
obj[key] = val;
},
};
Object.defineProperty(wrapper, "__v_isRef", {
value: true,
});
return wrapper;
}
function proxyRefs(target) {
return new Proxy(target, {
get(target, key, receiver) {
const value = Reflect.get(target, key, receiver);
return value.__v_isRef ? value.value : value;
},
set(target, key, newValue, receiver) {
const value = target[key];
if (value.__v_isRef) {
value.value = newValue;
return true;
}
return Reflect.set(target, key, newValue, receiver);
},
});
}
const newObj = proxyRefs({ ...toRefs(obj) });