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 }
)
选项 2:使用 computed
如果 manipulated
只是一个 read-only 道具,您可以从 ref
和 watch
切换到 computed
:
import { computed } from 'vue'
const props = defineProps(['data'])
let manipulated = computed(() => ({
a: props.data.a,
b: props.data.b,
}))
我有一个组件从 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 }
)
选项 2:使用 computed
如果 manipulated
只是一个 read-only 道具,您可以从 ref
和 watch
切换到 computed
:
import { computed } from 'vue'
const props = defineProps(['data'])
let manipulated = computed(() => ({
a: props.data.a,
b: props.data.b,
}))