第六章:原始值的响应式方案
原始值:Boolean、Number、String、undefined、null、BigInt、Symbol
ref
Proxy 只能代理对象,因此为了让原始值获得响应式,需要将其包裹
javascript
const wrapper = {
value: 'vue3'
};
const name = reactive(wrapper);
name.value = 'vue';
为了方便为原始值创建响应式
javascript
function ref(val) {
const wrapper = {
value: val
};
// 定义一个不可枚举的变量,用来区分数据是否是构建出的原始值对象
Object.defineProperty(wrapper, '__v_isRef', { value: true });
return reactive(wrapper);
}
响应丢失的问题
当执行解构的时候会出现响应丢失。
javascript
const obj = reactive({ foo: 1, bar: 2 });
const newObj = {
...obj
};
解决:创建一个 toRef 的方法,将 obj 转换为
javascript
function toRef(obj, key) {
const wrapper = {
get value() {
return obj[key];
}
};
Object.defineProperty(wrapper, '__v_isRef', { value: true });
return wrapper;
}
function toRefs(obj) {
const ret = {};
for (const k in obj) {
ret[k] = toRef(obj, k);
}
return ret;
}
const obj = reactive({ foo: 1, bar: 2 });
toRefs(obj)
// 转换后的结构
{
foo: {
get value() {
return obj.foo;
}
},
bar: {
get value() {
return obj.bar;
}
}
};
自动脱 ref
toRefs 解决了响应丢失的问题,但也带来了新的问题。
javascript
const obj = reactive({ foo: 1, bar: 2 });
const newObj = { ...toRefs(obj) };
// 访问属性时必须要加 .value
newObj.foo.value; // 1
尤其在模版中
html
<!-- 用户希望的样式 -->
{{foo}}
<!-- 现在的样子 -->
{{foo.value}}
所谓脱 ref 是指:如果读取的属性是一个 ref,那么自动返回 .value
javascript
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, newVal, receiver) {
const value = target[key];
if (value.__v_isRef) {
value.value = newVal;
return true;
}
return Reflect.set(target, key, newVal, receiver);
}
});
}
TIP
在 Vue.js 中
- setup 的返回值会传递给 proxyRefs 处理
- reactive 函数也有自动脱勾 ref 的能力
javascript
const count = ref(0);
const obj = reactive(count);
obj.count; // 0