You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的能力:

  1. ref的设计用途是获取DOM/组件实例引用,不是存储业务选中值。你写的:ref="(el) => {activeMealService = mealService.meal_type_id}"会在所有单选框完成挂载时依次执行,最后一个渲染的单选框的id会直接覆盖activeMealService的所有赋值,这个值在页面渲染完成后就固定了,和点击操作、选中状态没有任何关系。
  2. 你已经给单选框绑定了v-model="form.meal_type_id",选中状态的值本来就会自动同步到这个绑定字段上,不需要额外通过ref存储。
  3. 模板中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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.31 11:01:40