跳到主要内容

顶栏天气组件策略与实现说明

这篇文档记录顶栏天气组件的产品策略、实现思路和排障方式。

当前组件不是服务端天气组件,也不是显示服务器所在地天气。它在浏览器端运行,通过高德 JSAPI 尝试识别访问者所在城市,再查询该城市天气。

目标策略

天气组件采用三层策略:

  1. 页面加载后,优先通过高德 IP 定位识别访问者城市。
  2. 如果 IP 定位失败,或者 IP 定位得到的数据无法继续查询天气,则显示默认城市成都。
  3. 如果当前展示的是默认城市,用户点击天气组件时再请求浏览器地理位置权限;授权成功后切换到浏览器精确位置对应的天气。

如果一开始 IP 定位已经成功,用户点击组件只展开天气详情,不再请求浏览器地理位置。

这样做的原因是:

  • IP 定位不需要用户授权,适合静默初始化。
  • IP 定位可能失败,不能让组件因此消失。
  • 浏览器地理位置权限比较敏感,只有用户主动点击组件后才请求。
  • 默认城市能保证组件在公网、代理、海外网络、机房出口等场景下仍然可见。

默认城市配置为:

城市:成都市
adcode:510100

初始化流程

组件初始化时执行下面的流程:

加载高德 JSAPI
-> 加载 AMap.CitySearch 和 AMap.Weather 插件
-> 调用 CitySearch.getLocalCity()
-> 如果获得 city 或 province,使用 adcode/city 查询天气
-> 如果失败,使用成都市 510100 查询天气
-> 成功后渲染顶栏按钮并写入 sessionStorage 缓存

IP 定位成功时,天气数据会标记为:

source: ip

默认城市兜底成功时,天气数据会标记为:

source: default

这个 source 字段是后续点击策略的关键。组件通过它判断用户点击时是否需要请求浏览器地理位置。

点击流程

用户点击天气组件时,组件始终会先切换详情面板的展开状态。

随后根据当前天气来源决定是否请求浏览器定位:

  • source: ip:说明已经通过 IP 识别到用户城市,只展开详情,不请求权限。
  • source: browser:说明已经通过浏览器定位更新过,只展开详情,不重复请求权限。
  • source: default:说明当前只是成都兜底天气,首次点击时请求浏览器定位。

浏览器定位流程:

navigator.geolocation.getCurrentPosition()
-> 获取 WGS84/GPS 经纬度
-> 调用 AMap.convertFrom(..., 'gps') 转换为高德可用坐标
-> 加载 AMap.Geocoder
-> 调用 geocoder.getAddress() 反查城市和 adcode
-> 使用 adcode 查询实时天气和天气预报
-> 更新组件并刷新 sessionStorage 缓存

浏览器定位成功后,天气数据会标记为:

source: browser

如果用户拒绝权限、浏览器不支持定位、定位超时、逆地理编码失败,组件不会报错给用户,也不会隐藏。它会继续展示成都兜底天气。

为什么不能只依赖 IP 定位

IP 定位只能识别公网出口 IP,不等于用户真实 GPS 位置。

下面这些网络环境经常导致 IP 定位失败或不准:

  • 校园网、公司网、酒店网络等统一出口
  • 手机流量运营商 NAT 出口
  • VPN、代理、加速器
  • 海外网络或跨境出口
  • 云服务器、机房、CDN 或远程浏览器环境
  • 高德 IP 库暂未收录的地址段

线上排查时曾看到高德 IP 接口返回:

{
"status": "1",
"info": "OK",
"infocode": "10000",
"province": [],
"city": [],
"adcode": [],
"rectangle": []
}

这类响应代表请求本身成功,但没有可用城市数据。旧逻辑会因为拿不到城市而隐藏组件;新逻辑会回退到成都天气。

缓存策略

组件使用 sessionStorage 缓存天气数据:

key: navbar-weather-v2
ttl: 30 分钟

缓存目标是减少同一次浏览会话内重复请求高德接口。

缓存内容包括:

  • 城市名
  • adcode
  • 省份
  • 实时天气
  • 温度
  • 湿度
  • 风向风力
  • 预报列表
  • 数据来源 source
  • 报告时间

缓存过期或解析失败时会被忽略,不影响组件重新请求。

高德 JSAPI 依赖

组件依赖的高德插件包括:

  • AMap.CitySearch:用于 IP 定位。
  • AMap.Weather:用于实时天气和天气预报。
  • AMap.Geocoder:用于浏览器定位后的逆地理编码。

高德鉴权配置来自 Docusaurus customFields.amap

customFields: {
amap: {
key,
serviceHost,
securityJsCode,
},
}

组件只有在存在 key,且存在 serviceHostsecurityJsCode 时才会尝试加载高德 JSAPI。

组件隐藏条件

当前组件仍然会在这些情况下隐藏:

  • 移动端 navbar item 渲染路径传入 mobile
  • 缺少高德 JSAPI key 或安全配置。
  • 高德 JSAPI 无法加载,且没有可用缓存。
  • 成都兜底天气也查询失败,且没有可用缓存。
  • CSS 媒体查询命中 max-width: 996px

除此之外,单纯 IP 定位失败不再导致组件隐藏。

隐私与交互原则

浏览器精确定位只在用户点击天气组件后请求。

实现上不主动保存精确经纬度,也不把经纬度写入缓存。缓存里只保存查询天气所需的城市、adcode 和天气数据。

这保持了两个边界:

  • 页面初次加载不会打扰用户。
  • 授权定位只用于本次天气城市更新,不把精确位置长期留在站点存储里。

排障检查清单

如果线上天气组件没有出现,按下面顺序排查:

  1. 确认当前视口宽度大于 996px
  2. 确认页面已经加载 main.*.js,并包含 custom-weather 相关代码。
  3. 确认 window._AMapSecurityConfig 存在。
  4. 确认 window.AMap 已加载。
  5. 检查高德 JSAPI、插件和 REST 请求是否返回 200。
  6. 检查 sessionStorage.navbar-weather-v2 是否存在有效数据。
  7. 如果 IP 定位返回空城市,确认组件是否回退到 source: default 的成都天气。
  8. 如果点击后没有切换城市,检查浏览器是否拒绝了 geolocation 权限。

本地验证可以使用生产构建:

npm run build
npm run serve -- --host 127.0.0.1 --port 3002

在桌面宽度下访问页面,IP 定位失败时应至少看到成都天气。模拟或授权浏览器地理位置后,点击天气组件应切换到对应位置的天气。

相关实现文件

  • src/components/NavbarWeather/index.js
  • src/components/NavbarWeather/styles.module.css
  • src/utils/amap.js
  • docusaurus.config.js