leaflet L.popup().setContent中挂载vue组件

思路

Leaflet 的 L.popup().setContent(xxx) 中,xxx 只认 3 种合法值:

  • 纯文本字符串

  • HTML 字符串 (如 <div>船舶信息</div>)

  • 原生 JS DOM 节点 (核心!也是对接 Vue3 h()的关键)

Vue3 的渲染函数 h() 执行后,返回的是 Vue 虚拟节点 (VNode),不是原生 DOM,所以直接写setContent(h('div', {}, '内容')) 一定会报错;

我们的核心思路:把 h() 生成的虚拟节点vnode → 再通过render()挂载成「原生 DOM 节点」 → 再传给 setContent()。

代码

javascript 复制代码
  import PopupShipCompo from '../components/PopupShip/index.vue';
  
    // 获取地图
  function getMap() {
    !Boolean(shipMap) && initMap();
    return shipMap;
  }

  const popupShipInstance = ref<any>(null); // 船舶popup对象
  // 船舶popup中挂载的dom 的创建与销毁
  const popupShip: any = {
    dom: null,
    createDom: function () {
      if (!this.dom) {
        this.dom = document.createElement('div');
      } else {
        render(null, this.dom);
      }
      // 创建
      const vnode = h(PopupShipCompo, {
        // props
        currentClickShip: currentClickShip.value,
        // emit事件: 必须写成 on + 事件名大驼峰 格式!!
        onGoTrack: goTrack, // 子组件emit('goTrack')会触发这个方法
        onShipPopupClose: shipPopupClose
      });
      render(vnode, this.dom);
      return this.dom;
    },
    removeDom: function () {
      if (this.dom) {
        render(null, this.dom);
        this.dom.innerHTML = '';
      }
    }
  };
  // 显示船舶Popup
  const shipPopupShow = (latlng: any) => {
    shipPopupClose();
    popupShipInstance.value = L.popup({
      className: 'popup-ship-info',
      autoClose: false,
      closeOnClick: false,
      closeButton: false
    })
      .setLatLng(latlng)
      .setContent(popupShip.createDom());

    getMap().openPopup(popupShipInstance.value);
  };
  // 关闭船舶Popup
  const shipPopupClose = () => {
    getMap().closePopup(popupShipInstance.value);
  };
  onBeforeUnmount(() => {
    // 销毁dom
    if (popupShip.dom) {
      popupShip.removeDom();
    }
  });

/PopupShip/index.vue

javascript 复制代码
<template>
  ...
</template>

<script setup lang="ts">

  import { CloseOutlined, LineChartOutlined } from '@ant-design/icons-vue';
  import { Space, Steps, Row, Col } from 'ant-design-vue';
  const { Step } = Steps;

  const props = defineProps({
    currentClickShip: { type: Object, default: () => {} }
  });

  const emits = defineEmits(['goTrack', 'shipPopupClose']);

</script>

<style scoped></style>

dom销毁

Leaflet 的弹窗有个特性:手动关闭弹窗 / 切换弹窗时,弹窗的 DOM 会被 Leaflet 从页面移除,但不会自动销毁 Vue 的组件实例,如果不手动销毁,会造成「Vue 组件内存泄漏」,页面卡顿、事件重复触发等问题。

方案1:监听 Leaflet 弹窗关闭事件,销毁 Vue 组件(推荐,精准)

javascript 复制代码
 const leafletPopup = L.popup({...配置})
    .setLatLng(latlng)
    .setContent(popupDom)

  // ✅ 监听弹窗关闭事件,销毁Vue组件
  leafletPopup.on('remove', () => {
    render(null, popupDom) // 核心:传入null,卸载Vue组件,释放所有资源
  })

方案2:全局统一销毁

javascript 复制代码
onUnmounted(() => {
  ...
  allPopupDom.forEach(dom => render(null, dom))
})
相关推荐
大家的林语冰14 分钟前
CSS 已死?DOM 性能黑洞!Pretext 排版革命让你在文本间跳舞,没有 DOM 也能纵享丝滑~
前端·javascript·css
vipbic24 分钟前
我也该升级了,陪伴了我7年的博客
前端
Lee川44 分钟前
RAG 实战:从一篇掘金文章出发,拆解检索增强生成的全链路
前端·人工智能·后端
Lee川1 小时前
MCP 高德地图实战:当 AI 学会使用工具,一个协议如何重塑大模型的行动边界
前端·人工智能·后端
ZC跨境爬虫1 小时前
跟着 MDN 学CSS day_14:(尺寸调整技能测试与实战解析)
前端·css·ui·html·tensorflow
kyriewen1 小时前
用魔法打败魔法:我让AI替我去面试前端岗,AI面试官给我打了92分,还发了offer
前端·javascript·面试
IT_陈寒1 小时前
Redis批量删除踩了坑,原来DEL命令不是万能的
前端·人工智能·后端
lichenyang4532 小时前
鸿蒙聊天 Demo 练习 06:AI 思考气泡与 MVVM + Controller 结构重构
前端
Lkstar2 小时前
Vue keep-alive 原理全解:LRU 缓存策略、源码级理解
前端·vue.js·面试
会联营的陆逊2 小时前
html2canvas 1.4.1 在 iOS Safari 中生成图片卡住的问题排查与修复
前端