Vue3中Dynamic Refs使用问题:单选按钮点击始终返回相同值
Vue3 动态Ref(Dynamic Refs)用法与问题修复
动态Ref核心规则
Vue3中v-for批量渲染元素/组件时,无法给每个项绑定固定名称的ref,需要用动态ref实现引用收集,两种标准写法:
- 函数式ref:绑定一个回调函数,格式为
:ref="(el) => {}",元素挂载时回调会传入当前真实DOM/组件实例作为参数,元素卸载时参数为null - 数组自动收集(Vue 3.2+支持):直接将ref绑定为一个响应式数组,Vue会在渲染时自动将同绑定名的元素按渲染顺序存入数组
示例:批量收集列表项DOM
<script setup> import { ref, onMounted } from 'vue' const list = ref([{id:1, name:'A'}, {id:2, name:'B'}]) const itemDomList = ref([]) onMounted(() => { console.log(itemDomList.value) // 按顺序输出所有li的DOM对象 }) </script> <template> <ul> <li v-for="item in list" :key="item.id" :ref="itemDomList">{{item.name}}</li> </ul> </template>
现有代码问题说明
你当前代码返回值固定的核心原因是完全误用了ref的能力:
- ref的设计用途是获取DOM/组件实例引用,不是存储业务选中值。你写的
:ref="(el) => {activeMealService = mealService.meal_type_id}"会在所有单选框完成挂载时依次执行,最后一个渲染的单选框的id会直接覆盖activeMealService的所有赋值,这个值在页面渲染完成后就固定了,和点击操作、选中状态没有任何关系。 - 你已经给单选框绑定了
v-model="form.meal_type_id",选中状态的值本来就会自动同步到这个绑定字段上,不需要额外通过ref存储。 - 模板中label标签未闭合,会导致渲染异常。
正确实现代码
获取单选框选中值不需要使用动态ref,直接读取v-model绑定的字段即可:
<script setup> import { ref } from 'vue' const props = defineProps({ mealServices: Array, }); // 初始化form数据,如已在外部定义可忽略 const form = ref({ meal_type_id: '' }) const setActiveMealService = () => { // 直接读取v-model同步的选中值 console.log('meal id is ' + form.value.meal_type_id) }; </script> <template> <div v-for="mealService in mealServices" :key="mealService.id"> <input type="radio" name="meal_type" :id="mealService.meal_type_id" :value="mealService.meal_type_id" v-model="form.meal_type_id" @click="setActiveMealService" /> <label :for="mealService.meal_type_id">{{ mealService.meal_type.name }}</label> </div> </template>
如果确实需要通过动态ref获取所有单选框的DOM(用于DOM操作类需求,比如聚焦、样式修改),正确写法如下:
<script setup> import { ref, onBeforeUpdate } from 'vue' const props = defineProps({ mealServices: Array, }); const form = ref({ meal_type_id: '' }) // 存储DOM引用的数组 const radioDomList = ref([]) // 每次组件更新前清空数组,避免重复收集DOM onBeforeUpdate(() => { radioDomList.value = [] }) const setActiveMealService = () => { console.log('meal id is ' + form.value.meal_type_id) }; </script> <template> <div v-for="mealService in mealServices" :key="mealService.id"> <input :ref="(el) => { if(el) radioDomList.value.push(el) }" type="radio" name="meal_type" :id="mealService.meal_type_id" :value="mealService.meal_type_id" v-model="form.meal_type_id" @click="setActiveMealService" /> <label :for="mealService.meal_type_id">{{ mealService.meal_type.name }}</label> </div> </template>
注意事项
- 不要在动态ref的回调中存储业务数据,回调触发时机是DOM挂载/更新,和用户交互行为无关
- 用函数式ref收集v-for渲染的元素时,必须在组件更新前清空存储数组,否则会出现重复、过时的DOM引用
- 表单交互值优先通过v-model获取,不需要额外通过ref中转存储
内容的提问来源于stack exchange,提问作者user19106040
相关产品推荐
相关产品推荐

