Vue星级评分组件多实例渲染异常问题求助
Nuxt/Vue星级评分组件多实例异常问题修复
我开发了一个Vue(Nuxt)星级评分组件,单个实例使用正常,但同时使用多个指定ratingValue的实例时,仅最后一个组件的星级能正确着色,其余组件的星级显示异常。
组件代码如下:
<script setup lang="ts"> import { PropType } from "nuxt/dist/app/compat/capi"; interface Star { id: string; value: number; } const stars: Star[] = [ { id: "star5", value: 5 }, { id: "star4.5", value: 4.5 }, { id: "star4", value: 4 }, { id: "star3.5", value: 3.5 }, { id: "star3", value: 3 }, { id: "star2.5", value: 2.5 }, { id: "star2", value: 2 }, { id: "star1.5", value: 1.5 }, { id: "star1", value: 1 }, { id: "star0.5", value: 0.5 }, ]; const props = defineProps({ ratingValue: { type: Number as PropType<number>, }, starColor: { type: String as PropType<string>, }, }); const emit = defineEmits(["ratingChange"]); const selectedRating = ref<number | undefined>(undefined); const roundedRating = computed(() => { if (props.ratingValue !== undefined) { return Math.round(props.ratingValue * 2) / 2; } else { return selectedRating.value !== undefined ? Math.round(selectedRating.value * 2) / 2 : undefined; } }); const onRatingChange = (value: number) => { if (props.ratingValue === undefined) { selectedRating.value = value; emit("ratingChange", selectedRating.value); } }; </script> <template> <div class="center"> <div :class="['rating', { disable: props.ratingValue !== undefined }]"> <template v-for="star in stars" :key="star.id"> <input :id="star.id" v-model="roundedRating" :disabled="props.ratingValue !== undefined" type="radio" name="'rating'" :value="star.value" @input="onRatingChange(star.value)" /> <label :for="star.id" :class="star.value % 1 === 0 ? 'full full-star' : 'half'"></label> </template> </div> <div class="rating-number">{{ props.ratingValue }}</div> </div> </template>
使用示例:
<StarRating ratingValue="1" /> <StarRating ratingValue="4" />
问题原因
- 重复的radio name属性:所有组件实例的单选框都使用固定的
name="'rating'",导致多个组件的单选框被归为同一组,选中状态互相覆盖,最终仅最后一个实例的选中状态生效。 - 重复的id属性:每个星级的id是固定值(如
star5),多个组件实例会生成重复的id,违反HTML唯一性规则,导致label与input的关联混乱,无法正确触发选中样式。
修复方案
使用Vue3/Nuxt3提供的useId()生成唯一标识,为每个组件实例的单选框组和星级id添加唯一前缀,确保每个组件的表单元素独立。
修改后的组件代码:
<script setup lang="ts"> import { PropType, useId } from "nuxt/dist/app/compat/capi"; interface Star { id: string; value: number; } const stars: Star[] = [ { id: "star5", value: 5 }, { id: "star4.5", value: 4.5 }, { id: "star4", value: 4 }, { id: "star3.5", value: 3.5 }, { id: "star3", value: 3 }, { id: "star2.5", value: 2.5 }, { id: "star2", value: 2 }, { id: "star1.5", value: 1.5 }, { id: "star1", value: 1 }, { id: "star0.5", value: 0.5 }, ]; const props = defineProps({ ratingValue: { type: Number as PropType<number>, }, starColor: { type: String as PropType<string>, }, }); const emit = defineEmits(["ratingChange"]); const selectedRating = ref<number | undefined>(undefined); // 生成组件实例唯一标识 const uniqueId = useId(); const roundedRating = computed(() => { if (props.ratingValue !== undefined) { return Math.round(props.ratingValue * 2) / 2; } else { return selectedRating.value !== undefined ? Math.round(selectedRating.value * 2) / 2 : undefined; } }); const onRatingChange = (value: number) => { if (props.ratingValue === undefined) { selectedRating.value = value; emit("ratingChange", selectedRating.value); } }; </script> <template> <div class="center"> <div :class="['rating', { disable: props.ratingValue !== undefined }]"> <template v-for="star in stars" :key="star.id"> <input :id="`${uniqueId}-${star.id}`" v-model="roundedRating" :disabled="props.ratingValue !== undefined" type="radio" :name="`rating-${uniqueId}`" :value="star.value" @input="onRatingChange(star.value)" /> <label :for="`${uniqueId}-${star.id}`" :class="star.value % 1 === 0 ? 'full full-star' : 'half'"></label> </template> </div> <div class="rating-number">{{ props.ratingValue }}</div> </div> </template>
核心修改点
- 引入
useId()生成每个组件实例的唯一标识uniqueId - 为input的
id添加唯一前缀,避免多实例id重复 - 为radio的
name添加唯一前缀,确保每个组件的单选框组独立 - 同步修改label的
for属性,与input的id保持一致,保证关联正确
内容的提问来源于stack exchange,提问作者user20743167
相关产品推荐
相关产品推荐

