Vue3中Axios请求完成后Bootstrap5模态框数据加载异常问题
问题描述
编辑说明:我已发布最终找到的解决方案,供遇到相同问题的开发者参考。
我正在Vue3中开发一个可复用的Bootstrap 5模态框子组件,组件接收父组件传入的ID作为prop,通过Axios调用API获取数据,原本计划在请求完成后再渲染模态框。但现在模态框总是先显示空白白框,必须关闭重新打开才能看到加载完成的数据。我不清楚该如何确保数据完全加载后再显示模态框,或者无需刷新模态框就能正常加载数据。
子组件代码(ProjectPanel.vue)
<template> <div class="modal fade" id="projectPanel" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true" > <div class="modal-dialog modal-dialog-centered"> <div class="modal-content"> <div class="modal-body"> <PulseLoader v-if="isFetching"/> <div v-else class="row"> <div class="col text-center align-self-center"> <img class="image" v-if="imageUrl === ''" :src="projectDetail.thumbnailUrl" /> <img class="image" v-else :src="imageUrl" /> </div> <div class="col h-100"> <div class="text-start pb-2"> <h1>{{ projectDetail.name }}</h1> </div> <div class="text-start pb-2"> <h2>{{ projectDetail.createdTime }}</h2> </div> <div class="wording text-start"> <p> {{ projectDetail.description }} </p> </div> <div class="container"> <div class="row"> <img v-for="item in projectDetail.Images" :key="item.id" class="img my-2" @click="imageUrl = item.imageUrl" :src="item.imageUrl" /> </div> </div> </div> </div> </div> </div> </div> </div> </template> <script lang="ts"> import { defineComponent } from "vue"; import ProjectDataService from "@/services/ProjectDataService"; import ProjectDetail from "./Projects/ProjectDetail"; import ResponseData from "./Shared/ResponseData"; import PulseLoader from 'vue-spinner/src/PulseLoader.vue' export default defineComponent({ name: "ProjectPanel", props: ["id"], data() { return { projectDetail: {} as ProjectDetail, imageUrl: "", isFetching: true, }; }, components: { PulseLoader }, watch: { id: { immediate: true, async handler(val) { this.projectDetail = {} as ProjectDetail; if (val !== "" && val !== undefined) { await this.RetrieveProject(val as string); } }, } }, methods: { async RetrieveProject(id: string) { this.isFetching = true; var responseData = {} as ProjectDetail; // Retrieve Projects await ProjectDataService.getById(id) .then((res: ResponseData) => { responseData = res.data.data.project; }) .catch((e: Error) => { console.log(e); }) .finally(() => { this.projectDetail = responseData; }); this.isFetching = false; }, } }); </script>
父组件调用方式
<ProjectPanel :id="id" :key="id" />
解决方案
问题根源
- 模态框ID硬编码为固定值,当父组件通过
:key="id"重新渲染子组件时,DOM中会出现多个相同ID的模态框元素,导致Bootstrap无法正确识别和管理模态框实例。 - 模态框的显示时机与数据加载时机不匹配:模态框可能在数据未加载完成前就被打开,此时模板绑定的空数据会显示为空白,而数据加载完成后,用户需要重新打开才能看到更新后的内容。
具体修复步骤
1. 动态绑定模态框ID,避免DOM冲突
将模态框的id属性改为动态绑定,确保每个实例拥有唯一ID:
<template> <div class="modal fade" :id="`projectPanel-${id}`" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true" > <!-- 其余模板内容不变 --> </div> </template>
2. 调整数据加载与模态框显示的时序
让子组件内部管理模态框的显示逻辑,确保数据加载完成后再打开模态框:
子组件新增方法:
methods: { async loadProjectAndShowModal(projectId: string) { this.isFetching = true; try { const res: ResponseData = await ProjectDataService.getById(projectId); this.projectDetail = res.data.data.project; } catch (e: Error) { console.error('加载项目数据失败:', e); } finally { this.isFetching = false; // 数据加载完成后打开模态框 const modalElement = document.getElementById(`projectPanel-${projectId}`); if (modalElement) { const modal = new window.bootstrap.Modal(modalElement); modal.show(); } } } }
父组件调用方式修改:
通过ref调用子组件的方法,不再直接传递id prop和控制模态框显示:
<!-- 父组件模板 --> <button @click="handleOpenModal(project.id)">查看项目详情</button> <ProjectPanel ref="projectPanel" />
// 父组件脚本 methods: { async handleOpenModal(projectId: string) { await this.$refs.projectPanel.loadProjectAndShowModal(projectId); } }
3. 优化异步代码写法(可选)
避免async/await与.then()混用,让代码逻辑更清晰:
// 原RetrieveProject方法优化 async RetrieveProject(id: string) { this.isFetching = true; try { const res: ResponseData = await ProjectDataService.getById(id); this.projectDetail = res.data.data.project; } catch (e: Error) { console.error('获取项目数据失败:', e); } finally { this.isFetching = false; } }
额外说明
如果仍需要通过id prop触发数据加载,需确保模态框在数据加载完成后再初始化显示。可以在watch的回调中,数据加载完成后重新初始化模态框实例,避免Bootstrap缓存旧的DOM内容。
内容的提问来源于stack exchange,提问作者Chornologic
相关产品推荐
相关产品推荐

