dispose
本文回答:组件卸载 / 切换路由时正确的清理顺序,避免 Marker 残留、监听器泄漏、WebGL 上下文爆满。
一、标准清理顺序
// 卸载时按以下顺序:
// 1) 卸载 MapCadLayer(map.removeControl 会触发其 onRemove → 取消动画、释放 fx、清实体)
if (mapcadLayer) {
map.removeControl(mapcadLayer);
mapcadLayer = null;
}
// 2) 移除手动挂上的 Marker / Popup / 监听
markers.forEach(m => m.remove());
markers = [];
map.off("click", myHandler);
// 3) 销毁 Map(会释放 WebGL 上下文、source、layer、tile cache)
map.remove();
map = null;
// 4) Service 无 destroy 方法、本身无状态,不用管;多个 svc 实例共享底层连接也没问题
二、Vue 3
<script setup>
import { onMounted, onUnmounted } from "vue";
let map, mapcadLayer;
onMounted(async () => { /* 初始化见 integrations.md */ });
onUnmounted(() => {
mapcadLayer && map?.removeControl(mapcadLayer);
map?.remove();
map = null;
mapcadLayer = null;
});
</script>
Vue 2:用 beforeDestroy(或 Vue 2.7 的 beforeUnmount)。
三、React(StrictMode 友好)
useEffect(() => {
let map, cad, disposed = false;
(async () => {
// ... 初始化 ...
if (disposed) { map?.remove(); return; }
})();
return () => {
disposed = true;
if (cad && map) map.removeControl(cad);
map?.remove();
};
}, []);
React StrictMode 会双触发 useEffect,必须用 disposed 标志位防止第二次 map 在第一次还没建完就 remove。
四、常见泄漏症状
| 症状 | 泄漏点 | 修法 |
|---|---|---|
| Marker 在页面切换后还显示 | 没 marker.remove() 或持有 DOM 引用 |
组件卸载时显式 remove 全部 Marker |
| 切换页面多次后页面卡 | map.on("mousemove", ...) 不清 |
统一保存 handler 并 map.off |
切页面十次后抛 CONTEXT_LOST_WEBGL |
map 没 remove,WebGL 上下文超 16 个 | 卸载时必须 map.remove() |
MapCadLayer 的动画还在运行 |
没 removeControl(cad) |
先 removeControl,再 map.remove() |
mapdata watcher 还在触发 |
MapCadLayer.onRemove 没跑 |
确认 map.removeControl(cad) 成功调用 |
五、长驻应用的特殊情况
SPA 里长时间运行 / 地图频繁切换:
- 切图(切换另一张 DWG)不需要销毁 map,而是用
await map.switchMap({ mapid, mapopenway }, …)(内部 openMap + 换瓦片源 +updateMapExtent;只调updateMapExtent不会换瓦片,旧图照样显示),详见 open-map/switching.md。 - 不断新建 / 销毁 Map 实例会让浏览器 WebGL 上下文池满。每个页面 最多同时保留一张 Map。
六、配合 ResizeObserver 的清理
const ro = new ResizeObserver(() => map.resize());
ro.observe(container);
// 卸载时:ro.disconnect(); ro = null;
七、自动化检查清单
卸载时请确认以下都执行了:
-
map.removeControl(cad)(仅当用了MapCadLayer) - 所有手动
Marker.remove() - 所有手动
map.off(...) -
ResizeObserver.disconnect() -
map.remove() - 本地引用置
null - 组件内保留的
setInterval / setTimeout清除