思路
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))
})
