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

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" />
解决方案

问题根源

  1. 模态框ID硬编码为固定值,当父组件通过:key="id"重新渲染子组件时,DOM中会出现多个相同ID的模态框元素,导致Bootstrap无法正确识别和管理模态框实例。
  2. 模态框的显示时机与数据加载时机不匹配:模态框可能在数据未加载完成前就被打开,此时模板绑定的空数据会显示为空白,而数据加载完成后,用户需要重新打开才能看到更新后的内容。

具体修复步骤

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 19:22:43