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
相关产品推荐
相关产品推荐

