1. 架构概述
本项目采用高吞吐量、低开销的增量渲染架构来处理海量船舶和航线的展示。为了避免 Vue 响应式系统在处理海量 DOM 节点时的性能瓶颈,地图渲染核心完全与 Vue 的视图模板解耦,采用原生 TypeScript + Leaflet API 结合状态机缓存的策略。
整个模块采用分层解耦的设计:
- 基础设施层:
hifleet-loader.ts和animation.ts(负责 SDK 异步加载与requestAnimationFrame防抖)。 - 状态控制层(适配器):
useHifleetMap.ts(桥接 Vue 生命周期与 Leaflet 实例)。 - 渲染引擎层(核心):
hifleet-map-voyage-renderer.ts及其辅助文件(执行具体的增量 DOM 操作)。 - 组件展示层:
MapStage.vue(提供容器和事件抛出)。
2. 核心设计模式
2.1 增量更新与快照缓存 (Incremental Update & Snapshot Caching)
在 hifleet-map-voyage-renderer.ts 中,为每个航次维护了独立的 VoyageLayerState。每次渲染时,不会销毁重建图层,而是生成一组“快照签名 (Key)”与上一次比对:
renderedLineKey:包含了选中状态标识和轨迹坐标字符串。renderedPointKey:包含了地图层级、选中状态和可视区域内的轨迹点。renderedMarkerPositionKey:船舶经纬度字符串。renderedMarkerIconKey:包含船名、颜色、航向、选中状态。
优势:当航次属性未发生能导致视觉变化的改变时,直接 return 阻断 Leaflet DOM 操作,将性能损耗降至最低。
2.2 渲染风暴抑制 (Render Storm Coalescing)
地图的 zoom 和 pan 操作会在极短时间内触发成百上千次重绘事件。
在 animation.ts 中通过封装 requestAnimationFrame(即 createRafTask),确保在同一帧内,无论触发多少次渲染指令,都合并为一次执行。这在 useHifleetMap.ts 的 scheduleRefresh 函数中被完美应用。
2.3 实时数据的降级渲染 (Degraded Rendering for Realtime Data)
针对实时事件 VoyageRealtimeEvent:
- 若收到
voyage.point(点事件):意味着轨迹发生变化,调用全量的updateVoyageLayer重新计算轨迹线和点。 - 若收到
voyage.snapshot(快照事件):意味着仅仅是船舶移动或摘要数据变化,调用轻量的updateVoyageMarker仅更新图标位置和旋转角度,而不重算海量轨迹。
3. 模块职责边界
3.1 src/utils/map/hifleet-map-voyage-renderer.ts
职责:单例工厂,闭包维护了所有的 layerStates(状态集)和 iconCache(图标缓存)。
核心流程:
renderVoyages接收全量数据,比对增删。- 过滤掉离开地图视野的轨迹点。
- 调用
syncRouteLine、syncPointLayer和syncShipMarker进行细粒度的 DOM diff。 - 提供
clear方法在组件卸载时回收所有 Leaflet 内存。
3.2 src/utils/map/hifleet-map-icons.ts
职责:纯函数集合,负责生成 Leaflet 所需的 HTML 字符串 (divIcon)。 设计考量:
- 内部实现了
escapeHtml以防御 XSS 注入。 - 通过 CSS Variables (
--ship-color,--ship-rotation) 将状态变化交由浏览器硬件加速处理,而不是每次重写 HTML。
3.3 src/utils/map/hifleet-map-rendering.ts
职责:渲染辅助函数集。
设计考量:将序列化逻辑 (serializeLatLng)、颜色计算 (getPointColor) 和图层销毁逻辑 (removeMissingVoyageLayers) 抽离,保持 renderer 主文件的纯净。
3.4 src/composables/useHifleetMap.ts
职责:作为 Vue Store 和 Renderer 之间的适配器。 设计考量:
- 隐式处理了 HiFleet SDK 特有的
changeMap卫星图切换残留 Bug(引入global中间态)。 - 监听 DOM 的
ResizeObserver自动调用invalidateSize。 - 将 Leaflet 实例包裹在
shallowRef中,防止 Vue 递归劫持 Leaflet 内部庞大而复杂的对象树,引发极端的性能问题。
4. 后续扩展与维护指南
4.1 新增渲染层
如果需要新增渲染图层(如:渲染所有的停靠港口),请不要在现有的 hifleet-map-voyage-renderer.ts 中堆砌代码。应当参考该文件,新建一个 hifleet-map-port-renderer.ts,然后在 useHifleetMap.ts 中实例化它。保持不同业务实体的渲染器相互独立。
4.2 缓存键 (Cache Key) 维护
如果未来 VoyageSummary 新增了影响 UI 的属性(例如:根据吃水深度改变线条粗细),必须同步更新对应函数的 Key 生成逻辑。
例如:修改 syncRouteLine 中的 lineKey,加入吃水深度变量。否则图层将不会响应数据变化。
4.3 图标内存泄漏防护
目前图标采用了 Map 进行字符串级的单例缓存。如果未来产生了极度分散的变量(如每艘船都有完全不重复且无限生成的渐变色),可能导致 iconCache 内存无限膨胀。此时需引入 LRU (Least Recently Used) 算法或定期清理策略。