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

Vue.js中如何汇总所有单选按钮组选中值生成统一数组

问题根源

你现在的bug本质是Vue组件的作用域隔离规则:你在Question.vue的data里定义的arr数组,是每个Question组件实例独立持有的本地状态。你渲染了多少道题,就会生成多少个独立的Question实例,每个实例初始化时都会把自己的arr重置为[0,0,0,0,0],实例之间状态完全不共享,自然没法汇总所有题目的选择结果。
另外还有两个直接影响功能的附加问题:

  • 所有单选框的name属性都绑定成了同一个choices数组,会导致跨题目的单选互斥,根本无法实现每组题单独选一个选项的基础效果
  • onChange方法里e.target.name == i.choices的判断逻辑永远不会成立,等号两边一边是字符串类型、一边是数组类型,全等判断恒为false
修复方案

按照Vue单向数据流的规范,跨组件共享的汇总状态要上提到最顶层的公共祖先组件,也就是你的App.vue中,再通过props向下传递状态,通过自定义事件向上通知更新,不要在最底层的子组件里维护全局汇总数据。
具体修改步骤如下:

1. 修改App.vue

在根组件初始化答案数组,同时添加接收子组件更新答案的方法,将状态和方法传递给下层组件:

<template>
    HELLO
    <Panels :questions="questions" :answerArr="answerArr" @update-answer="updateAnswer"/>
</template>

<script>
import Panels from './components/Panels.vue';
import data from './questions.json';

export default{
    data(){
      return{
        questions: data,
        // 初始化和题目数量等长的答案数组,默认值填0即可
        answerArr: new Array(data.length).fill(0)
      }
    },
    methods: {
      updateAnswer(index, value) {
        // 用splice做响应式更新,不要直接通过索引修改数组
        this.answerArr.splice(index, 1, value)
        console.log('当前所有题的答案:', this.answerArr)
      }
    },
    components: {
      Panels
    }
}
</script>

<style>
</style>

2. 修改Panels.vue

只做props和自定义事件的透传即可,不要添加额外业务逻辑:

<template>
<div v-for="(question, index) in questions" :key="question.id">
    <Panel 
      :question="question" 
      :questionIndex="index"
      :answerArr="answerArr"
      @update-answer="(idx, val) => $emit('update-answer', idx, val)"
    />
</div>
</template>

<script>
import Panel from './Panel.vue'

export default{
    name: 'Panels',
    props: {
        questions: Array,
        answerArr: Array
    },
    components: {
        Panel
    }
}
</script>

<style scoped>
</style>

注意:v-for必须绑定唯一key,优先使用数据自带的唯一id字段,不要直接用索引当key,避免出现渲染错乱bug

3. 修改Panel.vue

同样做状态和事件透传,把当前题目的索引传给最底层的Question组件:

<template>
<div class = "Panel">
    <div>
        <p>{{question.question}}</p>
        <Question 
          :choices="question.choices" 
          :questionIndex="questionIndex"
          :correctAnswer="question.correctAnswer"
          @update-answer="(idx, val) => $emit('update-answer', idx, val)"
        />
        <p>当前题选择:{{$root.answerArr[questionIndex]}}</p>
    </div>
</div>
</template>

<script>
import Question from './Question.vue';

export default{
    name: 'Panel',
    props: {
        question: Object,
        questionIndex: Number,
        answerArr: Array
    },
    components: {
        Question
    }
}
</script>

<style scoped>
</style>

4. 重写Question.vue

删掉本地维护全局arr的逻辑,只维护当前题目的单选状态,选中时触发事件向上传值即可,同时修复单选框name重复的问题:

<template>
<div v-for="choice in choices" :key="choice">
    <!-- name绑定当前题的索引,保证同一题单选互斥、不同题互不干扰 -->
    <input 
        type="radio" 
        :id="`q-${questionIndex}-${choice}`" 
        v-model="mChoice" 
        :name="`question-${questionIndex}`" 
        :value="choice"
        @change="onChange"
    >
    <label :for="`q-${questionIndex}-${choice}`">{{choice}}</label><br>
</div>
<p>当前选中:{{mChoice}}</p>
</template>

<script>
export default{
    name: 'Question',
    data(){
        return{
            mChoice: null
        }
    },
    methods: {
        onChange(){
            // 直接把当前题索引和选中值抛给上层,不需要在子组件遍历所有题目
            this.$emit('update-answer', this.questionIndex, {
                idx: this.questionIndex,
                selected: this.mChoice,
                isCorrect: this.mChoice === this.correctAnswer
            })
        }
    },  
    props: {
        choices: Array,
        questionIndex: Number,
        correctAnswer: String
    }
}
</script>

<style scoped>
</style>
额外注意点
  • 你原有代码里的<style scope>是拼写错误,Vue的样式作用域关键字是scoped,拼错会导致样式隔离完全不生效
  • 子组件不要直接修改父组件/根组件传入的props,所有共享状态的更新都要通过事件通知上层修改,不然会出现响应式逻辑混乱的问题
  • 多组单选框必须给每组设置独立的name属性,不然浏览器默认的单选互斥逻辑会跨组生效,选了第二题的选项就会清空第一题的选中状态

内容的提问来源于stack exchange,提问作者chree

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 05:45:08