最近有一个想法,如何将reactive和computed混合起来?
意思是说,我希望有一个reactive对象,其中的某个属性依赖于其他的响应式数据。
假设有一个foo对象,它使用reactive封装,它其中的bar属性是一个计算属性,受到两个响应式数据a和b控制。
reactive+computed模式
const a = ref(1)
const b = ref(2)
const foo = reactive({
bar: computed(() => { return a.value + b.value }),
... // 其它属性
})
function showValue() {
console.log(foo.bar)
}
现在的问题是:
- 这个showValue会打印出什么?
- 修改a或b,foo.bar的值会变化吗?
为此我写了一段代码来测试:
答案是:
- foo.bar实际上会返回正确的a+b的值,也就是1+2=3。而不需要使用unref解包来取值,所以showValue会打印出3
- 修改a或b会影响foo.bar的值,也就是说这个过程不会导致响应式丢失。
reactive+getter模式
同时我又想到,是否可以省略掉computed,因为对象里本身就有一个取值写法:get-set模式,通过对象的getter属性也可以取到正确的值,于是有:
const foo2 = reactive({
get bar() { return a.value + b.value },
... // 其他属性
})
这样写的话,上面两个问题的答案还是原来的答案吗?
结果显而易见也是一样的。foo.bar的值依旧是a+b的结果,修改a或b依然会影响foo2.bar的值。
reactive+this
接下来有趣的来了,假设reactive对象引用自身的数据呢?
const foo3 = reactive({
a: 1,
b: 2,
get bar() { return this.a + this.b },
})
这种方式也可以获得一个响应式数据,并且foo3.bar能够正确引用到自身的a和b,并且响应a和b的变化。
get-set模式
最后,既然可以使用getter,同理也可以使用setter,这样一个双向响应式数据就可以完成了。
const foo3 = reactive({
a: 1,
b: 2,
get bar() {
return this.a + this.b;
},
set bar(val) {
this.a = Math.floor(val / 2);
this.b = Math.floor(val / 2);
},
});
加更
在reactive+computed模式下,我又发现了一种新的应用,就是在已有的reactive对象上通过使用computed来添加对象的属性,使得可以对reactive数据进行扩展。
const value = ref(0)
const obj = reactive({
value: ""
})
obj.otherValue = computed({
get() { return value.value },
set(val) { value.value = val },
})
这样做在外部看来obj.otherValue就像一个正常的属性一样,不需要通过.value来引用数据,vue3内部会自动进行解包操作。