Vue 3 中集成天地图 JavaScript API 的完整实践指南
本文详细介绍如何在 Vue 3 + Element Plus 项目中集成天地图 JavaScript API v4.0,涵盖 SDK 加载、地图选点定位、多边形绘制/编辑、轨迹展示等常见场景的完整实现方案。
一、为什么选择天地图
天地图(TianDiTu)是国家地理信息公共服务平台,提供标准的 JavaScript API,相较于国外地图服务具有以下优势:
- 国内访问稳定,加载速度快
- 提供卫星影像、混合地图等多种图层
- API 覆盖标记、折线、多边形、信息窗、比例尺等常用功能
- 完全免费,无需注册信用卡
二、SDK 动态加载方案
天地图 JS API 通过 CDN 加载,建议在组件内按需动态引入,避免首屏加载不必要的资源。
2.1 加载函数
1 | const TIANDITU_TK = "your_api_key_here"; |
2.2 关键设计点
| 设计 | 说明 |
|---|---|
| Promise 化 | 将异步加载封装为 Promise,便于 async/await 调用 |
| 幂等性 | 加载前检查 window.T,多次调用不会重复创建 <script> 标签 |
| 按需加载 | 仅在打开地图弹窗时触发,首页不加载地图 SDK |
| 错误处理 | onerror 回调 reject,调用方可以捕获加载失败并给出友好提示 |
三、核心场景实现
3.1 场景一:地图选点定位
在地图弹窗中点击任意位置,自动获取经纬度坐标。
1 | // 初始化地图 |
关键点:
- 使用
TMAP_SATELLITE_MAP卫星图层,方便辨认实际地物 - 标记的
iconAnchor设为图标底部中心,定位更准确 - 每次点击先移除旧标记再创建新标记,保证地图上只有一个标记点
3.2 场景二:多边形区域绘制
用户在地图上依次点击顶点,绘制多边形区域,支持撤销和清空操作。
3.2.1 数据结构
1 | // 存储所有顶点 |
3.2.2 点击绘制顶点
1 | map.addEventListener("click", e => { |
3.2.3 撤销与清空
1 | // 撤销上一点 |
3.2.4 自动计算中心点
1 | /** |
3.2.5 已有多边形回显展示
1 | const initDetailMap = async boundaryData => { |
关键点:
setViewport自动计算最优缩放级别和中心点,使多边形完整显示- 中心点使用自定义 SVG 图标,视觉上更突出
- InfoWindow 用于悬浮展示详细信息,交互体验好
- 数据驱动视图:先维护状态数组,再统一调用
redrawPolygon()重绘,撤销/清空操作简洁可靠
3.3 场景三:路径轨迹展示
展示从起点到终点的完整路径,使用折线 + 起终点标记。
1 | const initTrackMap = async trajectory => { |
起/终点 SVG 图标生成函数:
1 | /** |
关键点:
- 使用内联 SVG Data URI 生成自定义标记图标,无需额外静态资源
setViewport配合getBounds()自动适配,确保整条轨迹完整可见- 比例尺控件帮助用户直观感知距离
四、地图实例生命周期管理
每个使用地图的组件都应维护独立的实例引用,确保组件销毁时正确清理:
1 | // 模块级变量,存储地图实例引用 |
生命周期对照表:
| 事件 | 操作 |
|---|---|
弹窗 @opened | 调用 initMap() 创建地图 |
弹窗 @closed | 调用 destroyMap() 销毁实例 |
组件 onUnmounted | 清理可能残留的地图实例 |
五、API 能力速查
| API | 用途 | 关键参数 |
|---|---|---|
T.Map(containerId) | 创建地图实例 | DOM 容器 id |
map.centerAndZoom(center, zoom) | 设置中心点和缩放 | T.LngLat, 缩放级别 1-18 |
map.setMapType(type) | 设置图层 | TMAP_NORMAL_MAP / TMAP_SATELLITE_MAP / TMAP_HYBRID_MAP |
T.Marker(lngLat, opts) | 添加标记点 | 坐标、T.Icon 图标配置 |
T.Polyline(points, style) | 绘制折线 | 坐标数组、颜色、线宽 |
T.Polygon(points, style) | 绘制多边形 | 坐标数组、边框/填充样式 |
T.InfoWindow(content) | 信息窗 | HTML 内容字符串 |
map.setViewport(bounds) | 自适应视野 | polyline.getBounds() / polygon.getBounds() |
T.Control.Scale() | 比例尺控件 | 添加到 map.addControl() |
map.addEventListener(event, fn) | 事件监听 | click、mouseover、mouseout 等 |
map.clearOverLays() | 清除所有覆盖物 | 无 |
六、踩坑记录与最佳实践
6.1 弹窗中地图初始化时机
问题:在 el-dialog 中使用天地图,如果在 onMounted 中初始化,此时弹窗 DOM 可能还未渲染,地图容器 div 的实际宽高为 0,导致地图无法正常显示。
解决:监听弹窗的 @opened 事件,此时 DOM 已完全渲染,再初始化地图:
1 | <el-dialog @opened="initMap" @closed="destroyMap"> |
6.2 多次打开弹窗地图未重绘
问题:关闭弹窗后再次打开,地图容器中残留上次的 DOM 节点,两次初始化互相干扰。
解决:关闭弹窗时彻底销毁地图实例和 DOM:
1 | const destroyMap = () => { |
6.3 经纬度顺序陷阱
问题:天地图 API 中 LngLat 的构造函数参数顺序是 (经度, 纬度),即 (lng, lat),而非更常见的 (lat, lng)。如果搞反会导致标记点飞到错误位置。
正确写法:
1 | new T.LngLat(lng, lat); // ✅ 经度在前,纬度在后 |
6.4 多边形数据格式兼容
问题:后端返回的多边形数据格式可能不统一,有的是 [[lng, lat]] 二维数组,有的是 [{lng, lat}] 对象数组。
解决:编写兼容解析函数:
1 | const parseBoundary = data => { |
6.5 API Key 安全防护
注意:天地图 API Key 不应直接硬编码在前端代码中。建议方案:
- 将 Key 存储在
.env环境变量中:VITE_TIANDITU_TK=your_key - 使用
import.meta.env.VITE_TIANDITU_TK读取 - 生产环境可通过后端接口动态获取,进一步降低泄漏风险
七、进阶封装建议
当项目中有多个页面使用天地图时,建议将通用逻辑抽取为 Composable:
1 | // composables/useTdtMap.js |
使用示例:
1 | <template> |
八、总结
在 Vue 3 项目中落地天地图,核心在于以下几点:
- 按需加载:通过动态注入
<script>标签实现 SDK 按需加载,避免首屏性能损耗 - 生命周期对齐:弹窗
@opened初始化、@closed销毁,避免地图渲染异常 - 数据驱动视图:以状态数组为单一数据源,通过重绘函数统一渲染覆盖物,撤销/清空操作简洁可靠
- 适度抽象:当使用场景超过 2 个时,建议抽取 Composable 统一管理地图生命周期和操作
本文覆盖了选点定位、多边形绘制/编辑、路径轨迹展示三个典型场景,掌握了这些模式后,扩展到热力图、聚合点等高级功能也会更加得心应手。
本文基于天地图 JavaScript API v4.0 编写。