Vue3 变换道具结构保持反应性

Vue3 transform props structure maintaining reactivity

我有一个组件从 API 获取数据并将其传递给显示它的另一个组件(再次传递给多个子组件)。所以我尝试将该信息拆分为多个 ref 对象,以传递给每个子组件。

问题是,这样做会降低反应性...当父组件更改其数据时,新的 ref 对象不会更新。

我创建了一个示例来说明问题。

ParentComponent

<template>
  <p>All data: {{ data }}</p>
  <child-component :data="data"></child-component>
</template>
<script setup>
import { ref } from "vue";
import ChildComponent from "./ChildComponent.vue";

let data = ref({
  a: {},
  b: {},
  c: {}, // More properties...
});

setTimeout(() => { // FAKE API
  data.value.a = {
    name: "prop A",
    description: "aaa"
  };
  data.value.b = {
    name: "prop B",
    description: "bbb"
  };
  data.value.c = {
    name: "prop C",
    description: "ccc"
  };
  // More properties updated...
}, 2000);
</script>

ChildComponent

<template>
  <p>child component props: {{data}}</p>
  <p>child component manipulated: {{manipulated}}</p>
</template>

<script setup>
import { ref } from "vue";

const props = defineProps(['data'])

let manipulated = ref({ // Only need a and b properties.
  a: props.data.a,
  b: props.data.b
})

</script>

挂载组件时的输出

All data: { "a": {}, "b": {}, "c": {} }

child component props: { "a": {}, "b": {}, "c": {} }

child component manipulated: { "a": {}, "b": {} }

更新父组件数据后的输出:

All data: { "a": { "name": "prop A", "description": "aaa" }, "b": { "name": "prop B", "description": "bbb" }, "c": { "name": "prop C", "description": "ccc" } }

child component props: { "a": { "name": "prop A", "description": "aaa" }, "b": { "name": "prop B", "description": "bbb" }, "c": { "name": "prop C", "description": "ccc" } }

child component manipulated: { "a": {}, "b": {} }

更新父组件数据后的预期结果:

All data: { "a": { "name": "prop A", "description": "aaa" }, "b": { "name": "prop B", "description": "bbb" }, "c": { "name": "prop C", "description": "ccc" } }

child component props: { "a": { "name": "prop A", "description": "aaa" }, "b": { "name": "prop B", "description": "bbb" }, "c": { "name": "prop C", "description": "ccc" } }

child component manipulated: { "a": { "name": "prop A", "description": "aaa" }, "b": { "name": "prop B", "description": "bbb" } }

如何实现?使用基元可以工作......但是对于对象,缺少一些东西来保持反应性......

选项 1:使用深度观察器

您可以在 props.data 上使用带有 deep 标志的 deep watcher (i.e., watch,用 props.data 的新值更新 manipulated:

// ChildComponent.vue
import { watch } from 'vue'
⋮
watch(
  () => props.data,
  newValue => {
    const { a, b } = newValue
    manipulated.value = { a, b }
  },
  { deep: true }
)

demo 1

选项 2:使用 computed

如果 manipulated 只是一个 read-only 道具,您可以从 refwatch 切换到 computed

import { computed } from 'vue'

const props = defineProps(['data'])

let manipulated = computed(() => ({
  a: props.data.a,
  b: props.data.b,
}))

demo 2