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))
})
相关推荐
蓝帆傲亦18 小时前
前端性能极速优化完全指南:从加载秒开体验到丝滑交互
前端·交互
鱼毓屿御18 小时前
如何给用户添加权限
前端·javascript·vue.js
JustHappy18 小时前
「web extensions🛠️」有关浏览器扩展,开发前你需要知道一些......
前端·javascript·开源
何中应19 小时前
nvm安装使用
前端·node.js·开发工具
xixixin_19 小时前
【JavaScript 】从 || 到??:JavaScript 空值处理的最佳实践升级
开发语言·javascript·ecmascript
Java新手村19 小时前
基于 Vue 3 + Spring Boot 3 的 AI 面试辅助系统:实时语音识别 + 大模型智能回答
vue.js·人工智能·spring boot
雯0609~19 小时前
hiprint:实现项目部署与打印3-vue版本-独立出模板设计与模板打印页面
前端·vue.js·arcgis
杜子不疼.19 小时前
【Linux】教你在 Linux 上搭建 Web 服务器,步骤清晰无门槛
linux·服务器·前端
belldeep19 小时前
python:用 Flask 3 , mistune 2 和 mermaid.min.js 10.9 来实现 Markdown 中 mermaid 图表的渲染
javascript·python·flask
凉辰19 小时前
使用uni.createInnerAudioContext()播放指定音频(踩坑分享功能)
开发语言·javascript·音视频