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

v-for循环中动态传Props至组件时地图定位异常问题排查

问题根源及解决方案

核心问题分析

  1. 多弹窗复用同一控制变量:在v-for循环内为每个卡片都创建了q-dialog,且所有弹窗共用同一个mapDialog状态变量。当触发弹窗时,所有弹窗都会尝试激活,视觉上最后一个弹窗会覆盖前面的,导致始终显示最后一个会议的位置。
  2. 地图组件未响应Props更新:MeetMapComponent的mapRender()仅在组件初始化时执行一次,后续Props变化不会触发地图重新渲染,导致显示的是第一次初始化时的位置数据。
  3. 组件Key未关联会议实例:MeetMapComponent的key使用了全局的componentKey,没有绑定到当前会议,Vue会复用组件实例,无法更新为当前会议的位置数据。

解决方案步骤

1. 重构弹窗结构,使用单一弹窗+当前选中会议

将弹窗从v-for循环内移出,通过变量存储当前选中的会议,确保弹窗只渲染对应会议的地图数据:

父组件模板修改:

<!-- 单一弹窗放在循环外 -->
<q-dialog class="mapDialog flex column" v-model="mapDialog">
  <MeetMapComponent
    :mapData="currentMeeting?.address"
    :buyerName="currentMeeting?.name"
  />
</q-dialog>

<!-- 会议卡片循环 -->
<div
  v-for="meeting in meetings"
  :key="meeting.did"
  class="col-12 col-md-6 col-lg-3 q-pa-md q-mx-xl"
>
  <q-card class="my-card homeCard q-pa-md">
    <q-card-section
      class="tipCardImage flex row justify-end"
      :style="`background-image: url(${meeting.car.carImg})`"
    >
      <router-link
        :to="`/user/meet/edit/${meeting.did}`"
        style="text-decoration: none"
      >
        <q-icon
          @click="fetchMeeting(meeting.did)"
          name="fa-solid fa-pencil editNameIcon q-mb-sm q-ml-sm"
        ></q-icon>
      </router-link>
      <q-icon
        name="fa-solid fa-trash editNameIcon q-mb-sm q-ml-sm"
        @click="triggerDelete(meeting.did)"
      ></q-icon>
    </q-card-section>

    <q-card-section>
      <div class="cardTitle">
        <span>Encuentro Con</span> {{ truncateString(meeting.name, 30) }}
      </div>
      <div class="tipCardText">
        <span>Agendado para el </span>
        <p>{{ truncateString(meeting.date, 120) }}</p>
      </div>
      <div class="flex row justify-end">
        <q-btn
          @click="openMapDialog(meeting)"
          class="text-white cardButton"
          :class="{ cardButtonMobile: $q.screen.lt.md }"
          >Ver Ubicación</q-btn
        >
      </div>
    </q-card-section>
  </q-card>
</div>

父组件逻辑修改:

import { ref } from 'vue';

// 弹窗控制变量
const mapDialog = ref(false);
// 当前选中的会议
const currentMeeting = ref(null);

// 打开弹窗时赋值当前会议
function openMapDialog(meeting) {
  currentMeeting.value = meeting;
  mapDialog.value = true;
}

2. 修改地图组件,响应Props变化并清理实例

让MeetMapComponent监听Props变化,重新渲染地图,同时添加实例清理逻辑避免内存泄漏:

<template>
  <div class="meetMapContainer">
    <div ref="mapDiv" style="width: 100%; height: 500px" />
    <h5 class="text-center text-white">{{ props.mapData?.address }}</h5>
  </div>
</template>

<script setup>
import { ref, watch, onUnmounted } from "vue";
import { Loader } from "@googlemaps/js-api-loader";

const props = defineProps({
  mapData: Object,
  buyerName: String,
});

const GOOGLE_MAPS_API_KEY = "...";
const loader = new Loader({ apiKey: GOOGLE_MAPS_API_KEY });
const mapDiv = ref(null);
let mapInstance = null; // 存储地图实例用于销毁

async function mapRender() {
  // 无有效坐标时直接返回
  if (!props.mapData?.coordinates) return;

  // 清理旧地图实例
  if (mapInstance) {
    google.maps.event.clearInstanceListeners(mapInstance);
    mapInstance = null;
  }

  await loader.load();
  // 创建新地图实例
  mapInstance = new google.maps.Map(mapDiv.value, {
    mapTypeId: "roadmap",
    center: props.mapData.coordinates,
    zoom: 13,
  });

  // 添加标记
  new google.maps.Marker({
    position: props.mapData.coordinates,
    map: mapInstance,
    title: `Encuentro con ${props.buyerName}`,
  });
}

// 监听Props变化,触发地图重新渲染(immediate: true 确保初始化时执行)
watch([() => props.mapData, () => props.buyerName], mapRender, { immediate: true });

// 组件卸载时清理地图实例
onUnmounted(() => {
  if (mapInstance) {
    google.maps.event.clearInstanceListeners(mapInstance);
    mapInstance = null;
  }
});
</script>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 04:35:26