vue3中如何混合reactive和computed?

295 阅读2分钟

最近有一个想法,如何将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的值会变化吗?

为此我写了一段代码来测试:

stackblitz.com/edit/vitejs…

答案是:

  • 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内部会自动进行解包操作。