从 0 到上线 网站建设实训课
总览/ 课程地图/ S9 外部服务对接

M32在页面里加地图

所属路径 B / C 第 32 / 32 讲 S9 外部服务对接
客户说「要能看到我们在哪」,地图怎么加才不出事
学完能独立接入一家合规地图服务,并在它不可用时自动降级,而不是白屏

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 步操作顺序

  1. 定形态:内嵌地图 + 三家外链按钮都要(外链零依赖,是保底)
  2. 申请 key:腾讯 lbs.qq.com 选「JS GL / Web端」;高德 console.amap.com 选「Web端 JS API」,同时拿到安全密钥 jscode;两家都配域名白名单
  3. 把 key 写到服务器的 js/map-keys.v2.js(不入库),页面读不到就整块隐藏,不报错
  4. 给这个文件加 no-cache;换 key 时把文件名升一级(v2→v3),穿透浏览器里已缓存的旧文件
  5. 容器给固定高度,脚本懒加载 + POI 检索 + 一家挂了自动换另一家 + 最后显示「请用下方按钮导航」
  6. 三家外链都用坐标(高德是经度在前),发完按清单验收: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 属性里的 & 必须写成 &amp;,中文参数统一百分号编码。

实测跳转结果(都应 302 到落地页):腾讯 → tools/poimarker;高德 → ditu.amap.com/regeo?lng=..&lat=..;百度 → map.baidu.com/?latlng=..&autoOpen=true。发布前逐个点一遍。
不要用关键词搜索型链接(如 map.baidu.com/search/公司名)。实测百度会按访客 IP 所在城市检索,访客不在当地(尤其海外客户)就搜不到,页面看起来像「没有定位」。

5. 坐标怎么来:必须双源核实

  1. 腾讯 WebService:/ws/place/v1/search?keyword=关键词&boundary=region(城市,0)&page_size=1&key=…
  2. 高德 REST(需要「Web服务」类型的 key):/v3/place/text?key=…&keywords=关键词&city=城市
  3. 两家返回的 POI 名称与经纬度一致才采信,写进兜底坐标,并在代码注释里记下来源与日期
坐标属于「编错一个字就指到别人家」的数据:单源结果一律不采信,也不能凭地图上目测的位置手填。

6. 验收清单

  1. 页面能 grep 到三个外链(腾讯 / 高德 / 百度 marker)
  2. /js/map-keys.v2.js 返回 200,响应头带 Cache-Control: no-cache
  3. 真实浏览器滚到地图处:canvas ≥ 1、信息窗显示名称与地址、控制台 0 报错
  4. 两个标签都可见(说明 key 与安全密钥都配齐)
  5. 模拟故障:拦截地图 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 条

  • 白名单部署:只上传清单里列出的文件,不是整目录覆盖。清单通常要维护两处,容易不同步。