1、引入ref的概念
非对象的变量无法使用proxy进行代理,需要进行包裹, 由此引出ref
function ref(val) {
let wrapper = {
value : val,
}
return reactive(wrapper)
}
// 测试示例
const flag = ref(true)
effect(() => {
console.log(flag.value)
})
flag.value = false
// 测试结果
true
false
需要给ref一个标识, 方便后面脱ref
function ref(val) {
let wrapper = {
value : val,
}
Object.defineProperty(wrapper, "_v_isRef", {value: truef})
return reactive(wrapper)
}
2、响应丢失问题
这是涉及到实际应用
<template>
<p>{{ foo }} / {{ bar }}</p>
</template>
<script>
export default {
setup() {
// 响应式数据
const obj = reactive({ foo: 1, bar: 2 })
// 1s 后修改响应式数据的值,不会触发重新渲染
setTimeout(() => { obj.foo = 100 }, 1000)
return {...obj}
}
}
</script>
上面的展开运算符会导致响应丢失, 返回了一个普通对象
该怎么建立联系, 如下面代码
const obj = reactive({foo: 1, bar: 2})
const newObj = {
foo: {
get value() {
return obj.value;
}
},
bar: {
get value() {
return obj.bar;
}
}
}
// 测试示例
effect(() => {
console.log(newObj.bar.value);
})
obj.bar = 1;
// 测试结果
2
1
将上面的解构抽取并封装
// 单个
function toRef(obj, key) {
const wrapper = {
get value() {
return obj[key]
}
}
Object.defineProperty(wrapper, "_v_isRef", {value: true}); // 这里解构成了ref, 同样需要打上标记
return wrapper
}
// 多个
function toRefs(obj) {
const ret = {}
for(const key in obj) {
ret[key] = toRef(obj, key)
}
return ret
}
// 测试示例
const obj = reactive({foo: 1, bar: 2})
const newObj = { ...toRefs(obj)}
effect(() => {console.log(newObj.bar.value);})
obj.bar = 1;
// 测试结果
2
1
上面对于属性只有get方法, 没有set的方法
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
}
3、自动脱ref
上面的toRefs会把响应式数据的第一层属性值转换成ref, 因此必须要通过value属性访问值, 增加了用户的心智负担
newObj.bar.value
newObj.foo.value
因此需要一个自动脱ref的功能
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) {
let value = target[key];
if(value._v_isRef) return value.value = newValue;
Reflect.set(target, key, value, receiver)
}
})
}
const obj = reactive({foo: 1, bar: 2})
const newObj = proxyRefs({...toRefs(obj)})
effect(() => {console.log(newObj.bar);})
obj.bar = 1;
理解在setup的return返回的对象中, 底层包装了一层类似proxyRefs的方法, 实现了对象的解构,同时也不增加用户的心智负担