本文整理高德地图 Web JS API 2.0 在原生 JavaScript 和 Vue 3 项目中的接入方式,并给出点标记、信息窗体、定位、地址解析、路线规划等常见功能示例。
本文示例面向中国大陆版 Web JS API 2.0。Key、配额、计费与合规要求可能调整,上线前请以高德开放平台控制台和官方文档为准。
一、接入前准备
1.1 创建应用和 Key
- 登录高德开放平台控制台。
- 进入「应用管理」并创建应用。
- 在应用中添加 Key,服务平台选择「Web 端(JS API)」。
- 保存生成的
Key 和 安全密钥 securityJsCode。 - 在控制台配置可使用该 Key 的域名白名单,并为开发、测试、生产环境分别创建 Key。
2021 年 12 月 2 日之后申请的 Web 端 Key 必须配合安全密钥使用。注意不要误选「Web 服务」Key,Web 服务 Key 主要供服务端 REST API 使用。
1.2 安全密钥配置
开发环境可直接配置明文安全密钥,但它最终会出现在浏览器代码中:
1 2 3
| window._AMapSecurityConfig = { securityJsCode: '你的安全密钥', };
|
该配置必须在加载 JS API 之前执行。生产环境推荐通过自己的服务器代理服务请求:
1 2 3
| window._AMapSecurityConfig = { serviceHost: 'https://map-api.example.com/_AMapService', };
|
其中 /_AMapService 是高德规定的固定前缀。代理服务器负责将请求转发到高德并附加 jscode,不要把安全密钥下发给浏览器。官方提供了 Nginx、Java、Node 等代理思路,可参考安全密钥使用说明。
二、原生 JavaScript 快速接入
官方推荐使用 JS API Loader,加载器可以处理异步加载、插件加载和重复请求。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| <!doctype html> <html lang="zh-CN"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>高德地图示例</title> <style> html, body, #map-container { width: 100%; height: 100%; margin: 0; } </style> </head> <body> <div id="map-container"></div>
<script> window._AMapSecurityConfig = { securityJsCode: '你的安全密钥', }; </script> <script src="https://webapi.amap.com/loader.js"></script> <script> AMapLoader.load({ key: '你的 Web 端 Key', version: '2.0', plugins: ['AMap.Scale', 'AMap.ToolBar'], }) .then(AMap => { const map = new AMap.Map('map-container', { viewMode: '2D', zoom: 13, center: [116.397428, 39.90923], mapStyle: 'amap://styles/normal', });
map.addControl(new AMap.Scale()); map.addControl(new AMap.ToolBar()); }) .catch(error => { console.error('高德地图加载失败:', error); }); </script> </body> </html>
|
地图容器必须已经挂载到 DOM,并且具有明确高度;只有 width: 100% 而没有高度时,地图不会显示。
三、npm 项目接入
3.1 安装 Loader
1
| npm install @amap/amap-jsapi-loader
|
创建统一的加载模块,避免业务组件各自加载不同版本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import AMapLoader from '@amap/amap-jsapi-loader';
let loadPromise;
export function loadAMap() { if (!loadPromise) { window._AMapSecurityConfig = { securityJsCode: import.meta.env.VITE_AMAP_SECURITY_CODE, };
loadPromise = AMapLoader.load({ key: import.meta.env.VITE_AMAP_KEY, version: '2.0', plugins: ['AMap.Scale', 'AMap.ToolBar', 'AMap.Geolocation', 'AMap.Geocoder', 'AMap.Driving'], }); }
return loadPromise; }
|
环境变量示例:
1 2 3
| VITE_AMAP_KEY=你的开发环境Key VITE_AMAP_SECURITY_CODE=你的开发环境安全密钥
|
环境变量只能改善配置管理,不能隐藏发送到浏览器的秘密。生产环境请使用域名白名单和代理方案,不要把 .env 当作安全措施。
四、Vue 3 完整接入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| <template> <div ref="containerRef" class="map-container"></div> </template>
<script setup> import { nextTick, onBeforeUnmount, onMounted, ref } from 'vue'; import { loadAMap } from '@/utils/amap';
const containerRef = ref(null);
let AMap = null; let map = null; let resizeObserver = null;
onMounted(async () => { try { await nextTick(); AMap = await loadAMap();
map = new AMap.Map(containerRef.value, { viewMode: '2D', zoom: 13, center: [116.397428, 39.90923], resizeEnable: true, });
map.addControl(new AMap.Scale()); map.addControl(new AMap.ToolBar({ position: 'RB' }));
const marker = new AMap.Marker({ position: [116.397428, 39.90923], title: '天安门', }); map.add(marker);
resizeObserver = new ResizeObserver(() => map?.resize()); resizeObserver.observe(containerRef.value); } catch (error) { console.error('地图初始化失败:', error); } });
onBeforeUnmount(() => { resizeObserver?.disconnect(); map?.destroy(); map = null; AMap = null; }); </script>
<style scoped> .map-container { width: 100%; height: 600px; } </style>
|
在弹窗、折叠面板或 keep-alive 页面中,容器显示状态或尺寸改变后可调用:
五、地图实例常用操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| map.setCenter([121.473667, 31.230525]); map.setZoom(15); map.setZoomAndCenter(15, [121.473667, 31.230525]);
const center = map.getCenter(); const zoom = map.getZoom();
const bounds = new AMap.Bounds([116.2, 39.7], [116.6, 40.1]); map.setBounds(bounds);
function handleMapClick(event) { console.log(event.lnglat.getLng(), event.lnglat.getLat()); }
map.on('click', handleMapClick); map.off('click', handleMapClick);
|
常用事件包括 click、dblclick、rightclick、moveend、zoomend 和 complete。
六、覆盖物
6.1 点标记 Marker
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| const marker = new AMap.Marker({ position: [116.397428, 39.90923], title: '天安门', anchor: 'bottom-center', offset: new AMap.Pixel(0, 0), draggable: true, });
map.add(marker);
marker.on('click', event => { console.log('点击标记:', event.lnglat); });
marker.setPosition([116.407428, 39.90923]); map.remove(marker);
|
多个覆盖物可一次添加,并自动调整视野:
1 2 3 4 5 6 7 8 9 10
| const markers = points.map( point => new AMap.Marker({ position: [point.lng, point.lat], title: point.name, }), );
map.add(markers); map.setFitView(markers, false, [60, 60, 60, 60]);
|
点位达到数百或数千个时,不要创建大量复杂 DOM Marker,可根据需求选择 LabelsLayer、MassMarks 或聚合能力。
6.2 信息窗体 InfoWindow
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| const infoWindow = new AMap.InfoWindow({ content: ` <div class="map-info-window"> <strong>天安门</strong> <p>北京市东城区长安街</p> </div> `, anchor: 'bottom-center', offset: new AMap.Pixel(0, -35), });
marker.on('click', () => { infoWindow.open(map, marker.getPosition()); });
infoWindow.close();
|
如果 content 包含用户输入,必须先转义或过滤,避免 XSS。
6.3 折线、圆和多边形
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| const polyline = new AMap.Polyline({ path: [ [116.38, 39.9], [116.4, 39.91], [116.42, 39.9], ], strokeColor: '#1677ff', strokeWeight: 6, });
const circle = new AMap.Circle({ center: [116.397428, 39.90923], radius: 1000, fillColor: '#1677ff', fillOpacity: 0.15, strokeColor: '#1677ff', });
const polygon = new AMap.Polygon({ path: [ [116.38, 39.9], [116.41, 39.9], [116.41, 39.92], [116.38, 39.92], ], fillColor: '#52c41a', fillOpacity: 0.2, strokeColor: '#52c41a', });
map.add([polyline, circle, polygon]); map.setFitView([polyline, circle, polygon]);
|
七、浏览器定位
使用定位功能前加载 AMap.Geolocation 插件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| const geolocation = new AMap.Geolocation({ enableHighAccuracy: true, timeout: 10000, zoomToAccuracy: true, position: 'RB', });
map.addControl(geolocation);
geolocation.getCurrentPosition((status, result) => { if (status === 'complete') { const { lng, lat } = result.position; map.setZoomAndCenter(16, [lng, lat]); console.log('定位成功:', result); return; }
console.error('定位失败:', result.message, result); });
|
浏览器精确定位通常要求:
- 页面使用 HTTPS,
localhost 开发环境除外; - 用户授予位置权限;
- 浏览器、系统和网络环境支持定位;
- 业务对拒绝授权、超时和弱精度结果提供降级方案。
八、地址与坐标互转
使用前加载 AMap.Geocoder 插件。
8.1 地址转经纬度
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| const geocoder = new AMap.Geocoder({ city: '北京', });
geocoder.getLocation('北京市东城区天安门', (status, result) => { if (status === 'complete' && result.info === 'OK') { const location = result.geocodes[0]?.location; if (!location) return;
const position = [location.getLng(), location.getLat()]; map.setZoomAndCenter(16, position); console.log('地址坐标:', position); } else { console.error('地理编码失败:', result); } });
|
8.2 经纬度转地址
1 2 3 4 5 6 7
| geocoder.getAddress([116.397428, 39.90923], (status, result) => { if (status === 'complete' && result.info === 'OK') { console.log(result.regeocode.formattedAddress); } else { console.error('逆地理编码失败:', result); } });
|
九、驾车路线规划
页面增加结果面板:
1 2
| <div id="map-container"></div> <div id="route-panel"></div>
|
加载 AMap.Driving 插件后执行搜索:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| const driving = new AMap.Driving({ map, panel: 'route-panel', hideMarkers: false, autoFitView: true, });
const start = new AMap.LngLat(116.379028, 39.865042); const end = new AMap.LngLat(116.427281, 39.903719);
driving.search(start, end, (status, result) => { if (status === 'complete') { console.log('路线规划完成:', result); } else { console.error('路线规划失败:', result); } });
driving.clear();
|
其他路线规划插件:
| 场景 | 插件 |
|---|
| 驾车 | AMap.Driving |
| 公交 | AMap.Transfer |
| 步行 | AMap.Walking |
| 骑行 | AMap.Riding |
| 货车 | AMap.TruckDriving |
十、插件的按需加载
初始化时不确定会使用哪些功能,可以在业务触发后加载:
1 2 3 4 5 6 7
| AMap.plugin(['AMap.Geocoder', 'AMap.PlaceSearch'], () => { const geocoder = new AMap.Geocoder(); const placeSearch = new AMap.PlaceSearch({ city: '北京', pageSize: 10, }); });
|
同一页面不要混用 JS API 1.4.x 和 2.0,也不要同时用 Loader 与手写 <script> 重复加载 API。
十一、坐标系说明
高德地图使用 GCJ-02(火星坐标系)。常见数据源可能使用其他坐标系:
| 数据来源 | 常见坐标系 |
|---|
| GPS、国际通用经纬度 | WGS84 |
| 高德、腾讯、国内 Google | GCJ-02 |
| 百度地图 | BD-09 |
如果标记整体偏移数百米,优先检查坐标系。不要只通过手动增减经纬度“校准”,应使用合规的坐标转换接口。经纬度数组的顺序是 [longitude, latitude],即 [经度, 纬度]。
十二、常见问题排查
12.1 地图空白
依次检查:
- 容器是否有非零高度,创建地图时容器是否已挂载。
- 浏览器控制台和 Network 中是否有鉴权或资源加载错误。
- Key 是否为「Web 端(JS API)」类型,是否配置了正确域名。
window._AMapSecurityConfig 是否在 Loader 或 JS API 之前设置。- 页面 CSP、广告拦截器或公司代理是否拦截了高德资源。
- 弹窗或 Tab 从隐藏切换为显示后,是否调用了
map.resize()。
12.2 INVALID_USER_KEY 或鉴权失败
- Key 填写错误或已被删除、禁用;
- Key 类型不匹配;
- 域名白名单不包含当前域名;
- Key 与安全密钥不属于同一个应用;
- 安全配置执行得太晚;
- 调用量或服务权限超出控制台限制。
12.3 AMap is not defined
不要在 AMapLoader.load() 完成之前使用 AMap:
1 2
| const AMap = await loadAMap(); const map = new AMap.Map(container);
|
12.4 Vue 路由切换后报错或内存上涨
组件卸载时销毁地图、Observer 和自行注册的监听:
1 2 3 4 5 6
| onBeforeUnmount(() => { resizeObserver?.disconnect(); map?.off('click', handleMapClick); map?.destroy(); map = null; });
|
12.5 定位失败
确认页面为 HTTPS、浏览器有位置权限,然后记录 status、result.message 和完整错误对象。定位属于外部能力,产品上应允许用户手动选点或输入地址。
十三、上线检查清单
十四、参考资料