loading

Vue - reactive 解构赋值丢失响应式,toRef 维持响应式

丢失响应式

reactive 创建的响应式数据被解构赋值之后在模板中使用会丢失响应式。因为结构之后的属性没有了那一层 get 和 set 函数,具体原因请查看我的另一篇博文:理解 Vue 深层次响应式和浅层响应式

file:[App.vue]
<script setup>
// 解构赋值
const { foo, bar } = { ...reactive({ foo: 1, bar: { val: 1 } }) };
</script>
<template>
  <div>foo: {{ foo }}</div>
  <button @click="foo++">Change foo</button>
  <div>bar val: {{ bar.val }}</div>
  <button @click="bar.val++">Click bar val</button>
</template>

如下图所示,从 reactive 函数创建的对象中结构出来的 foo 变量不论点击多少次视图都没有反应。只有在也是同样由 reactive 函数创建的对象中结构出来的 bar.val 更新之后才更新。

图1 - 解构赋值失去响应式

为什么点击第二个按钮会让 foo 更新?

这与 reactive 创建深层对象有关,在遇到一个嵌套对象时,reactive 会递归地创建这个对象的响应式代理。所以,解构得到的 bar 还是代理对象,因此还保留着响应式能力。

关于深层次和浅层次响应式的知识点请阅读:理解 Vue 深层次响应式和浅层响应式

foo 因为解构失去了 get 和 set 函数,也就失去了响应式的能力,而还有响应式能力的 bar 对象还是代理对象,也就还有 get 和 set 函数,foo 因为前几次更改,在被其他还有响应式能力的数据而更新视图之后也一并更新了,所以 foo 就显示了最新的值。

延续响应式

toReftoRefs基于响应式对象上的一个或多个属性,延续其响应式能力,与源响应式数据进行链接。

tip:[start]toReftoRefs 基于响应式对象的,也就是说不是基于一个普通的对象的。tip:[end]

file:[App.vue]
<script setup>
import { reactive, toRefs } from "vue";

const obj = reactive({ foo: 1, bar: { val: 1 } });
const { foo, bar } = toRefs(obj);

function changeFoo() {
  foo.value++;
}

function changeBar() {
  bar.value.val++;
}
</script>
<template>
  <div>foo: {{ foo }}</div>
  <button @click="changeFoo">Change foo</button>
  <div>bar: {{ bar.val }}</div>
  <button @click="changeBar">Change bar</button>
</template>

通过 toRefs,解构赋值得到的 foo 和 bar 都延续了响应式能力。不会丢失响应式能力,foo 和 bar 与源响应式数据 obj 进行了链接,所以,直接操作 obj 也会反馈给 foo 和 bar,反之亦然,这就是官方文档的案例所说的那样。

图2 - 延续响应式能力

用于 prop

组件 prop 不是 Ref 类型,当组合式 API 需要传递 Ref 类型时,可以通过 toRef 返回这一个对应的 Ref 类型给函数。

file:[Test.vue]
<script setup>
const props = defineProps(/* ... */)

// 将 `props.foo` 转换为 ref,然后传入一个组合式函数
useRefHistory(toRef(props, "foo"));
</script>

tip:[start]以上的使用场景,并不是把它转换成了一个 Ref 响应式数据。toRef 是延续响应式能力,而不是创建响应式数据。上面的例子中只是转换 foo 的类型,让组合式 API 能够通过类型校验,而不报错。tip:[end]

更具体的例子,请阅读我另一篇博文:Vue3 watch 函数监听子组件 props 的变化

总结

toRef 或者 toRefs 都是延续响应式能力,而绝对不是创建响应式数据。refreactive 才是创建响应式数据的 API。

如果使用了解构赋值,丢失了响应式能力,就可以用 toReftoRefs 延续响应式能力,以至于在模板中可以继续使用 Vue 的特性。

posted @ 2023-03-19 00:13  Himmelbleu  阅读(708)  评论(0编辑  收藏  举报