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

Vue Bootstrap模态框重复渲染:点击编辑按钮弹出多模态框

问题:点击表格行编辑按钮弹出多个模态框

我用Vue Bootstrap的<b-table>组件创建表格,通过Vuex从外部JSON文件获取学生数据。表格每行都会渲染一个Actions组件,组件里的编辑按钮点击后本该打开一个模态框,但现在一点击就弹出4个叠加的模态框——数量正好对应JSON里的4条学生数据,删掉3条后就只弹出1个,显然是每行都渲染了模态框,不知道怎么修复。

原代码

Table组件

<script>
import Actions from "./Actions.vue"

export default {
    data() {
        return {
            fields: [
                'index',
                'full_name',
                { key: "date_of_birth", label: 'Date of Birth' },
                'municipality',
                { key: "action", label: 'Action' }
            ],
            // tableItems: this.$store.state.registeredStudents.registeredStudents
        }
    },

    components: {
        Actions
    },

    methods: {
        generateIndex() {
            return Math.floor(1000000 * Math.random()).toString().slice(0, 6);
        }
    },

    computed: {
        rows() {
            return this.tableItems.length
        },

        tableItems() {
            const registeredStudents = this.$store.state.registeredStudents.registeredStudents

            return registeredStudents.map(student => ({
                index: this.generateIndex(), ...student
            }))
        }
    },
}
</script>

<template>
    <b-table :fields="fields" :items="tableItems" :per-page="perPage" :current-page="currentPage" responsive="sm" primary-key="index"
        striped hover>
        <template #cell(action)="data">
            <Actions/>
        </template>
    </b-table>
</template>

Actions组件

<script>
import { BIconPencilFill, BIconTrashFill } from 'bootstrap-vue';

export default {

}
</script>

<template>
    <div>
        <b-button variant="primary" class="mx-1 p-1" v-b-modal.edit-student>
            <b-icon-pencil-fill></b-icon-pencil-fill>
        </b-button>
        <b-modal id="edit-student" title="Edit student info">
            <p class="my-4">Hello from modal!</p>
        </b-modal>
        <b-button variant="danger" class="mx-1 p-1">
            <b-icon-trash-fill></b-icon-trash-fill>
        </b-button>
    </div>
</template>

解决方案

问题核心是Actions组件被每行重复渲染,导致页面中存在多个ID为edit-student的模态框,Bootstrap Vue的v-b-modal指令通过ID匹配模态框,所有同名ID的模态框都会被触发,因此出现多个弹窗叠加的情况。

修复步骤:

1. 把模态框移到父级组件,只渲染一次

将Actions组件内的<b-modal>抽离到Table组件中,确保整个页面只有一个模态框实例。

2. 传递行数据,点击时更新模态框内容

  • 给Actions组件添加student属性,接收当前行的数据
  • Actions组件点击编辑按钮时,通过自定义事件通知父组件打开模态框并传递数据
  • 父组件监听事件,更新模态框内的展示内容

修改后的代码

Table组件

<script>
import Actions from "./Actions.vue"

export default {
    data() {
        return {
            fields: [
                'index',
                'full_name',
                { key: "date_of_birth", label: 'Date of Birth' },
                'municipality',
                { key: "action", label: 'Action' }
            ],
            currentStudent: null, // 存储当前要编辑的学生数据
            showEditModal: false   // 控制模态框显示状态
        }
    },

    components: {
        Actions
    },

    methods: {
        generateIndex() {
            return Math.floor(1000000 * Math.random()).toString().slice(0, 6);
        },
        openEditModal(student) {
            this.currentStudent = student;
            this.showEditModal = true;
        }
    },

    computed: {
        rows() {
            return this.tableItems.length
        },

        tableItems() {
            const registeredStudents = this.$store.state.registeredStudents.registeredStudents

            return registeredStudents.map(student => ({
                index: this.generateIndex(), ...student
            }))
        }
    },
}
</script>

<template>
    <div>
        <b-table :fields="fields" :items="tableItems" responsive="sm" primary-key="index" striped hover>
            <template #cell(action)="data">
                <!-- 传递当前行数据到Actions组件,监听编辑事件 -->
                <Actions :student="data.item" @edit="openEditModal"/>
            </template>
        </b-table>

        <!-- 仅渲染一次的模态框,绑定当前学生数据 -->
        <b-modal v-model="showEditModal" title="Edit student info">
            <div v-if="currentStudent" class="my-4">
                <p>姓名: {{ currentStudent.full_name }}</p>
                <p>出生日期: {{ currentStudent.date_of_birth }}</p>
                <p>城市: {{ currentStudent.municipality }}</p>
            </div>
        </b-modal>
    </div>
</template>

Actions组件

<script>
import { BIconPencilFill, BIconTrashFill } from 'bootstrap-vue';

export default {
    props: {
        student: {
            type: Object,
            required: true
        }
    },
    methods: {
        handleEdit() {
            // 触发父组件的编辑事件,传递当前学生数据
            this.$emit('edit', this.student);
        }
    }
}
</script>

<template>
    <div>
        <!-- 点击触发自定义事件,不再使用v-b-modal -->
        <b-button variant="primary" class="mx-1 p-1" @click="handleEdit">
            <b-icon-pencil-fill></b-icon-pencil-fill>
        </b-button>
        <b-button variant="danger" class="mx-1 p-1">
            <b-icon-trash-fill></b-icon-trash-fill>
        </b-button>
    </div>
</template>

额外提示

generateIndex方法在tableItems计算属性中每次都会重新生成随机索引,可能导致表格行频繁重渲染,建议改用学生数据中的唯一标识(如ID)作为主键,或者在数据初始化时生成索引,避免不必要的性能损耗。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 04:15:39