M32在页面里加地图
客户说「要能看到我们在哪」,地图怎么加才不出事
学完能独立接入一家合规地图服务,并在它不可用时自动降级,而不是白屏
01为什么重要
地图是客户第一眼判断「你在不在」的组件,也是最容易出事的一类:key 泄露、白名单没配、浏览器不支持 WebGL 直接白屏、外链搜不到。它的价值不在「画出来」,而在「画不出来时怎么退」——地图挂了页面不能跟着挂。
02要讲清的 5 件事核心要点
- 服务商只能用合规白名单:腾讯位置服务 / 高德 / 百度 / 天地图。境外地图(Google Maps、Apple Maps、Bing 境外版、Mapbox、OSM 境外瓦片)一律不用,这是合规红线,不是偏好问题
- key 绝不进 git(仓库历史会永久留痕),只写在服务器上的 js/map-keys.*.js,该文件不入库;页面读不到 key 就整块隐藏 —— 降级不能变成报错
- 外链一律带坐标,不用关键词搜索:关键词会按访客 IP 所在城市检索,海外客户直接搜不到,页面看起来像没定位
- 内嵌容器必须有固定高度,否则不渲染;腾讯 GL 依赖 WebGL,老显卡 / 远程桌面 / 关了硬件加速会白屏且控制台不报错,必须探测后自动切高德
- 坐标要双源核实(腾讯 WebService + 高德 REST 返回值一致才采信),不手工编造;三家经纬度顺序不同,高德是「经度,纬度」
03照着做:6 步操作顺序
- 定形态:内嵌地图 + 三家外链按钮都要(外链零依赖,是保底)
- 申请 key:腾讯 lbs.qq.com 选「JS GL / Web端」;高德 console.amap.com 选「Web端 JS API」,同时拿到安全密钥 jscode;两家都配域名白名单
- 把 key 写到服务器的 js/map-keys.v2.js(不入库),页面读不到就整块隐藏,不报错
- 给这个文件加 no-cache;换 key 时把文件名升一级(v2→v3),穿透浏览器里已缓存的旧文件
- 容器给固定高度,脚本懒加载 + POI 检索 + 一家挂了自动换另一家 + 最后显示「请用下方按钮导航」
- 三家外链都用坐标(高德是经度在前),发完按清单验收:canvas ≥1、控制台 0 报错、拦截 SDK 能自动切
# key 只放服务器,不进仓库(每台各一份) /var/www/<站点>/js/map-keys.v2.js # 这个文件会变、文件名又带不上 ?v=,只能靠响应头禁缓存 location = /js/map-keys.v2.js { add_header Cache-Control "no-cache, must-revalidate" always; } # 验收:数 canvas 比肉眼看可靠 chrome --headless=new --disable-gpu --virtual-time-budget=15000 \ --dump-dom "https://你的域名/contact" | grep -c "<canvas"
04完整 SOP照着能做
目标:联系页上放一张能用的地图,并且在地图服务挂掉时页面仍然完整可用。
这份 SOP 来自一次真实接入:客户要在联系页放地图。全程踩过 WebGL 白屏、浏览器缓存旧 key、百度外链按访客城市检索这几个坑,下面按能照做的顺序写。
三条铁律,违反就是合规风险或线上事故:① 地图服务商只能用合规白名单(腾讯位置服务 / 高德 / 百度 / 天地图),境外地图一律不用;② 真实 key 绝不进 git,仓库历史会永久留痕;③ 外链一律用坐标,不用关键词。
0. 先判断:你要加的是哪一种地图
| 形态 | 说明 | 是否需要 key |
|---|---|---|
| 内嵌地图 | 页面里直接显示可缩放的底图 + 标记 + 信息窗,可切换服务商 | 需要(Web端 / JS API 类型) |
| 外链按钮 | 点击跳到厂商网页或 App 并定位到该点 | 不需要(官方 URI 免 key) |
两个都要做:外链是保底(零依赖、永不失败),内嵌是体验(需要 key,可能失败,由自动切换兜底)。
1. 申请 key
腾讯位置服务:控制台 lbs.qq.com → 创建应用 → 添加 Key,类型选支持「JavaScript GL / Web端」的那一项;拿到 Key 后在设置里填域名白名单,把所有对外域名逐个列全。
高德开放平台:控制台 console.amap.com → 应用 → 添加 Key → 服务平台选「Web端 (JS API)」。这个 key 会同时生成安全密钥 jscode —— JS API 2.0 必须有它,缺了页面上的高德标签会自动隐藏。
key 类型不能串用:「Web服务」类型的 key 只能调 REST,不能用于页面内嵌;反之「Web端」key 调 REST 会被拒。判断方法见第 7 节速查表。
2. 把 key 放到服务器(不进 git)
在每台服务器上写 /var/www/<站点>/js/map-keys.v2.js:
window.SITE_MAP_KEYS = {
tencent: "YOUR_TENCENT_KEY",
amap: "YOUR_AMAP_KEY",
amapSecret: "YOUR_AMAP_JSCODE"
};
页面用 <script src="js/map-keys.v2.js"></script> 读取。任何一项为空或缺失 → 对应标签自动隐藏、页面外观不变,属于零风险降级。
这个文件不进 git,也打不上 ?v= 版本戳,所以要让浏览器立刻拿到新 key,必须两步都做:① nginx 对该路径禁缓存;② 换 key 时把文件名升一级(v2 → v3),同步改 HTML 里的 src。
location = /js/map-keys.v2.js {
add_header Cache-Control "no-cache, must-revalidate" always;
}
实测教训:只加 no-cache 头不够 —— 浏览器里已经缓存过的旧文件仍会在启发式有效期内被直接复用,表现为「同一个页面在 A 域名正常、B 域名失败」。改文件名是唯一可靠办法。
3. 内嵌地图组件
<div id="mapEmbed" style="display:none">
<div role="tablist">
<button id="mapTabTencent" role="tab">腾讯地图</button>
<button id="mapTabAmap" role="tab">高德地图</button>
</div>
<div id="mapBox" style="height:360px;border-radius:12px;overflow:hidden"></div>
</div>
容器必须有固定高度(这里 360px),否则地图不渲染 —— 尤其在 flex 布局里,高度塌陷时你会以为是 SDK 没加载。
| 机制 | 作用 |
|---|---|
| 懒加载 | IntersectionObserver(rootMargin 300px)滚到附近才加载 SDK,省流量与配额 |
| POI 检索 | 腾讯 searchRegion、高德 PlaceSearch 按关键词检索;坐标由地图库返回,不手工编造 |
| 本地缓存 | 检索结果存 localStorage,减少配额消耗 |
| 兜底坐标 | 检索失败但 SDK 可用时,用双源核实过的坐标渲染,位置仍然正确 |
| SDK 加固 | 脚本 8 秒超时 + 自动重试一次;onload 后轮询全局变量再检索 |
| 自动换一家 | 某家不可用(SDK 没出来 / 渲染抛错 / 需要 WebGL 而浏览器不支持)自动切另一家并翻转标签选中态 |
| 最后兜底 | 两家都失败才显示「地图服务加载失败,请用下方按钮导航」 |
腾讯 GL JS 依赖 WebGL。访客禁用硬件加速、老显卡、远程桌面环境下,new TMap.Map() 会白屏且不报错。必须在渲染前探测 WebGL,不支持就判腾讯不可用、自动切高德(2D 不需要 WebGL)。
4. 外链按钮(三家都要带坐标)
# 腾讯:marker URI,GCJ-02 https://apis.map.qq.com/uri/v1/marker?marker=coord:31.230416,121.473701;title:公司名;addr:地址&referer=你的站点标识 # 高德:position 是 经度,纬度(与腾讯顺序相反) https://uri.amap.com/marker?position=121.473701,31.230416&name=公司名&address=地址&src=你的站点标识&coordinate=gaode&callnative=1 # 百度:location 传 GCJ-02,由百度自行转 BD-09 https://api.map.baidu.com/marker?location=31.230416,121.473701&title=公司名&content=地址&output=html&coord_type=gcj02&src=你的站点标识
(示例坐标取上海人民广场,仅示范格式与顺序。)HTML 属性里的 & 必须写成 &,中文参数统一百分号编码。
实测跳转结果(都应 302 到落地页):腾讯 → tools/poimarker;高德 → ditu.amap.com/regeo?lng=..&lat=..;百度 → map.baidu.com/?latlng=..&autoOpen=true。发布前逐个点一遍。
不要用关键词搜索型链接(如 map.baidu.com/search/公司名)。实测百度会按访客 IP 所在城市检索,访客不在当地(尤其海外客户)就搜不到,页面看起来像「没有定位」。
5. 坐标怎么来:必须双源核实
- 腾讯 WebService:/ws/place/v1/search?keyword=关键词&boundary=region(城市,0)&page_size=1&key=…
- 高德 REST(需要「Web服务」类型的 key):/v3/place/text?key=…&keywords=关键词&city=城市
- 两家返回的 POI 名称与经纬度一致才采信,写进兜底坐标,并在代码注释里记下来源与日期
坐标属于「编错一个字就指到别人家」的数据:单源结果一律不采信,也不能凭地图上目测的位置手填。
6. 验收清单
- 页面能 grep 到三个外链(腾讯 / 高德 / 百度 marker)
- /js/map-keys.v2.js 返回 200,响应头带 Cache-Control: no-cache
- 真实浏览器滚到地图处:canvas ≥ 1、信息窗显示名称与地址、控制台 0 报错
- 两个标签都可见(说明 key 与安全密钥都配齐)
- 模拟故障:拦截地图 SDK 域名(返回 404)→ 应自动切另一家并渲染;把 getContext('webgl') 覆写成 null → 同样应自动切
# 无头浏览器快速体检:数 canvas,比肉眼看可靠
chrome --headless=new --disable-gpu --window-size=1400,9000 \
--virtual-time-budget=15000 --dump-dom "https://你的域名/contact" > dom.html
grep -c "<canvas" dom.html # ≥1 说明底图渲染了
7. 故障排查速查表
| 现象 | 原因 | 判定 / 处理 |
|---|---|---|
| 高德标签不显示 | key 缺失,或安全密钥 jscode 没填 | 页面逻辑:key 存在且 jscode 非空才显示 |
| 高德报 INVALID_USER_KEY(10001) | key 不存在 / 已删 / 未生效 | REST 探测返回 10001 就是废 key,重建 |
| 高德 REST 返回 USERKEY_PLAT_NOMATCH(10009) | key 是「Web端」类型(这其实是好消息) | Web端 key 本来就不能调 REST,用它做内嵌即可 |
| 腾讯地图白屏、不报错 | 浏览器不支持 WebGL | 代码已自动切高德;访客可开启「使用硬件加速」 |
| 同一份代码 A 域名正常、B 域名失败 | B 域名下浏览器缓存了旧 key 文件 | 改文件名(v2/v3)穿透缓存,再让用户强刷 |
| 点百度外链后「没有定位」 | 链接是关键词搜索,按访客 IP 城市检索 | 换成带坐标的 marker 链接 |
| 改了 key 但页面没变 | 浏览器或代理缓存 | 先确认响应头,再考虑改名;服务器侧用 curl 复核实际内容 |
05产出物与验收做完怎么算过
产出物
内嵌地图 + 三家外链按钮 + 一份「key 放哪、谁维护、怎么换」的说明
验收判据
可验页面 canvas ≥ 1、信息窗显示名称与地址、控制台 0 报错;把地图 SDK 拦成 404 后能自动切另一家并仍渲染
人评能说清为什么外链不带关键词、为什么 key 不进仓库
06练习15 分钟内可做完
在开发者工具里把地图 SDK 的域名拦成 404 并刷新,确认它自动切到另一家、地图仍然出来;再把 getContext('webgl') 覆写成返回 null,确认同样能切 —— 两条都是「不报错但白屏」的真实故障。
07最容易踩的坑
外链用关键词搜索型链接(如 map.baidu.com/search/公司名),海外客户点开一片空白
后果:腾讯 GL 在没 WebGL 的机器上白屏且控制台不报错——客户看到一片空白,以为官网坏了;或者 key 硬编码进仓库,泄露后配额被刷爆,账单来了才发现。
08自检表进度存在本机浏览器
0 / 9
09本讲术语全部 28 条
- 白名单部署:只上传清单里列出的文件,不是整目录覆盖。清单通常要维护两处,容易不同步。