我在 Vue.js 应用程序中遇到一个不寻常的问题,涉及对象上的双向数据绑定。
在我的设置中,父组件将一个对象作为 prop 传递给子组件。子组件在单击事件时更新该对象的属性。观察者正确更新值,然后调用父函数 (updateSelectedOptions),这是预期的行为。但是,子组件的观察程序再次运行并将该值恢复为其原始状态。仅在第一次单击时才会发生这种情况。
父组件:
<template>
<ShopProductOptions
:selected-options="selectedOptions"
@update:selectedOptions="updateSelectedOptions"
/>
</template>
<script setup>
const selectedOptions = ref({});
const updateSelectedOptions = (newSelections) => {
console.log('Updating selected options in parent:', newSelections);
selectedOptions.value = newSelections;
};
</script>
子组件(ShopProductOptions):
<template>
<div v-for="(option, index) in options" :key="index">
<span
class="option-item"
@click="setSelectedOption(option.name, value)"
>
{{option.name}}
</span>
</div>
</template>
<script setup>
const props = defineProps({
options: Array,
selectedOptions: Object
});
const localSelectedOptions = ref({ ...props.selectedOptions });
const emit = defineEmits(['update:selectedOptions']);
const setSelectedOption = (optionName, value) => {
localSelectedOptions.value[optionName] = value;
emit('update:selectedOptions', localSelectedOptions.value);
};
watch(localSelectedOptions, (newVal, oldVal) => {
console.log('Watcher triggered in child:', newVal, oldVal);
}, { deep: true });
</script>
问题:
首次单击某个选项时,将调用父组件中的
updateSelectedOptions
方法,更新 selectedOptions
。紧接着,子组件中的观察者再次被触发,恢复到之前的值。
此问题仅发生在第一次点击时;后续点击会按预期更新值,而不会进行任何还原。
我很困惑为什么这一系列事件和由此产生的回归只发生在第一次交互时。这是否与父级和子级之间嵌套对象的初始同步有关,或者 Vue 反应系统的另一个方面在这里起作用?任何见解或解决方案将不胜感激!
因为您使用解构来初始化子组件中的
localSelectedOptions
而不是引用。
使用
ComputedRef
代替 Ref
可以清楚地管理父子组件中共享的值,而且可以直接查看父组件中的值,而不是子组件中的副本
在你的子组件中
<script setup>
const props = defineProps({
options: Array,
selectedOptions: Object
});
const emit = defineEmits(['update:selectedOptions']);
// before
const localSelectedOptions = ref({ ...props.selectedOptions });
const setSelectedOption = (optionName, value) => {
localSelectedOptions.value[optionName] = value;
emit('update:selectedOptions', localSelectedOptions.value);
};
// after
const localSelectedOptions = computed({
get () {
return props.selectedOptions;
},
set (value) {
emit('update:selectedOptions', value);
}
});
const setSelectedOption = (optionName, value) => {
localSelectedOptions.value[optionName] = value;
};
// before
watch(localSelectedOptions, (newVal, oldVal) => {
console.log('Watcher triggered in child:', newVal, oldVal);
}, { deep: true });
// after
watch(() => props.selectedOptions, (newVal, oldVal) => {
console.log('Watcher triggered in child:', newVal, oldVal);
}, { deep: true });
</script>