Vue3 子UI不更新
问题描述
我在使用 v-model
时遇到了困难。
基本上,我有一个 Parent
组件,它可以拥有多个 Child
实例。单独使用Child正常工作,我希望Parent能够跟踪Child的所有值,并以某种自定义的格式输出值数组。
这种方式有点奏效,但是删除子组件时会出现一些奇怪的行为。
当我移除第一个条目时,数据如预期中的是 ['']
. 但是在视觉上,我看到的是错误的子组件被移除了。有什么想法是怎么回事吗?
Child.vue
<script setup>
import { ref, watch, defineProps } from 'vue';
const { modelValue } = defineProps(['modelValue']);
const emitUpdate = defineEmits(['update:modelValue']);
const category = ref(0);
const foods = ref(0);
// Emit new value when user picks a new food
watch(foods, () => {
emitUpdate('update:modelValue', `{category.value}{foods.value}`);
});
</script>
<template>
<div>
<select v-model="category">
<option value="Fruits">Fruits</option>
<option value="Pasta">Pasta</option>
</select>
<select v-model="foods">
<option value="oranges">Oranges</option>
<option value="lasagna">Lasagna</option>
</select>
</div>
</template>
Parent.vue
<script setup>
import Child from './Child.vue';
import { ref, defineProps } from 'vue';
const props = defineProps(['modelValue']);
const emit = defineEmits(['update:modelValue']);
const children = ref([]);
function addChild() {
children.value.push({ value: '' });
}
function removeChild(index) {
children.value.splice(index, 1);
emit('update:modelValue', children.value.map(child => child.value));
}
function updateChildValue(index, value) {
children.value[index].value = value;
emit('update:modelValue', children.value.map(child => child.value));
}
</script>
<template>
<div>
<button @click="addChild">Add child</button>
<div v-for="(child, index) in children" :key="index">
<Child v-model="child.value" @update:modelValue="updateChildValue(index, $event)" />
<button @click="removeChild(index)">Remove child</button>
</div>
</div>
</template>
使用方法
import Parent from '@/components/Parent.vue';
const myParentValue = ref('')
<template>
...
PARENT
<Parent v-model="myParentValue"></Parent>
myParentValue: {{ myParentValue }}
...
</template>
...
解决方案
不要将index作为你的v-for
指令的key。key应该是唯一的值。当你的数组有元素被添加或移除时,每个index上的项可能会改变,所以index不是给定项的唯一标识符。这就是为什么UI没有正确更新的原因。Vue不能根据key正确地区分数组的变化。 。我建议在添加一个新的子元素时添加一个唯一的id
字段。这是一种可能简单的实现方式:
let count = 0
function addChild() {
children.value.push({ id: count++, value: '' });
}
然后只需要更新你的 v-for:
<div v-for="(child, index) in children" :key="child.id">
还发现了另外两个不重要的问题:
首先,在Child.vue中:
const { modelValue } = defineProps(['modelValue']);
你不应该解构 defineProps
。这样做可能会导致响应性丢失。最好将 defineProps
的返回值赋给一个变量,例如 const props = defineProps
。目前你的代码中并不会造成问题,因为你在 Child 组件中实际上并没有使用 modelValue…如果你愿意的话,可以删除这个 prop,以及父组件中 <Child>
元素上的 v-model
,因为它们实际上并没有做任何事。真正起作用的是 emits。
其次,这不是一个问题,但你不需要导入 defineProps
,它在 script setup 中是自动可用的。
所有的修正都可以在这个 Vue Playground 示例中看到。