地图 SDK 串行阻塞定位与静态资源缓存策略 (Map SDK Serial-Blocking Location & Static Asset Cache Strategy)
2026-09-02 navi 页启动提速实践:定位被 1.19MB 地图 SDK 下载串行拖住 + 静态文件no-cache每次重新协商。两个问题一条链路排查、分头修复,提交49e17aa,线上验证通过。
问题与现象
- 进入导航页定位加载慢(低带宽环境尤其明显):地图已经渲染好了,学校桥定位却要等几秒才出结果。
- 每次打开 navi 页静态资源都重新下载/重新协商,带宽低更放大。
- 用户起初怀疑"加载了一个 1M 左右的 SDK"。
条件与根因(源码级确认)
根因 1:定位被地图 SDK 串行阻塞。
- 启动链:
loadBaiduMap()→ 注入<script src="api.map.baidu.com/api?v=1.0&type=webgl&ak=...">→ 返回 751 字节"引导壳" → 壳内再注入主库getscript?type=webgl&v=1.0&ak=...&t=20260824103905→ 主库 1,246,930 字节 ≈ 1.19MB → 再拉 3 个 worker →onCampusBaiduApiReady回调 →initializeMap()→ 才locateWithFallback()。 locateWithFallback()开头if (!state.mapReady) return null—— mapReady 要等 BMapGL 初始化完。所以定位被 1.19MB 下载串行堵住。- 关键认知:学校桥定位(
window.android.getMyLocationAsyn,返回 BD09)+ 后端逆地理(/reverse,fetch)都不需要 BMapGL。只有normalizeLocation里的BMapGL.Point/Convertor和渲染 marker 依赖地图 SDK。
根因 2:静态资源 no-cache。
- Rust
web.rs对/navi/*静态资源设no-cache(每次回源校验),低带宽下每次都重新下载 1MB+ 主包。
解决方案
定位解耦:提前定位不依赖地图 SDK
// 启动即并行(纯 JS,不依赖 BMapGL):
// 学校桥定位 + /reverse 逆地理 → 存 state.earlyLocation
// 地图就绪后 flushEarlyLocation() → new BMapGL.Point + acceptLocation 渲染
// 提前定位未完成/失败则走原 locateWithFallback 链兜底
startEarlyLocation(); // 与 loadBaiduMap() 并行
loadBaiduMap();要点:
state.earlyLocation/state.earlyLocating两个状态字段。- 地图就绪后
flushEarlyLocation()优先消费;!state.earlyLocating时才走 fallback 兜底。 - 复用
window.CampusLocate.schoolBridge+reverseGeocode(均不需 BMapGL)。
缓存策略:三档分级
| 资源 | Cache-Control | 原因 |
|---|---|---|
/navi/*?v=<版本戳> | public, max-age=31536000, immutable | 版本戳变 URL 就变,不会拿旧代码,免重复下载主包 |
/navi/*(无 v=) | no-cache | 兜底,版本戳忘更新也能拿到新代码 |
index.html | no-store | WebView 整页缓存会坏 |
// web.rs fallback:按 is_html / is_versioned_navi / is_navi 三分支设置 Cache-Control
let is_versioned_navi = is_navi
&& !is_html
&& uri.query().is_some_and(|q| {
q.split('&').any(|kv| kv.strip_prefix("v=").is_some_and(|v| !v.is_empty()))
});验证方法
- Rust:
cargo test --workspace,crates/campus-api/tests/http.rs三档缓存各有断言(26/26)。 - 前端:
npm run test:navi契约测试 17/17。 - 线上:
curl --resolve navi.6767.chat:443:124.221.137.194 https://navi.6767.chat/navi/app.js?v=...检查cache-control返回immutable;不带?v=返回no-cache;index.html返回no-store。 - 真机:低带宽下进入导航页,定位应秒出(不等 1.19MB SDK)。
部署注意(服务器实操坑)
- 非交互 SSH shell 不加载 rustup PATH:
cargo命令报 not found,需export PATH="$HOME/.cargo/bin:$PATH"。 - systemctl 必须 sudo:非交互下直调
systemctl报Interactive authentication required。 - 增量编译:
CARGO_BUILD_JOBS=2 cargo build --release -p campus-api(本次 1m26s)。 - 换二进制流程:备份
target/release/campus-api.bak.<ts>→sudo systemctl stop→cp新二进制 →sudo systemctl start。
可迁移的通用判断
- "页面渲染好了但 X 功能慢"优先查启动串行链:确认 X 是否被某个重资源(尤其第三方 SDK 主包)在调用时序上堵住。
- 识别不依赖 SDK 的能力:学校桥定位 + 后端逆地理其实不碰 BMapGL,只是代码把定位排在 mapReady 之后。把不依赖重依赖的请求提前并行,是通用的启动优化手法。
- 版本戳驱动的静态资源就该 immutable:URL 带内容版本 → 长缓存零风险;"每次回源校验"是保守但不是最优。
- 文档查不到的 SDK 参数,扒官方发布的压缩源码:
noAnimation就是如此发现的(见关联笔记)。
关联
- BMapGL 视角跟随 setHeading 内部动画与 noAnimation 瞬转 (BMapGL View-Follow setHeading noAnimation Snap) —— 同链路前一次修复,视角跟随卡顿根因 +
{noAnimation:true}瞬转。 - BMapGL 视角跟随航向插帧 (BMapGL View-Follow Heading Interpolation) —— 废弃的插帧方案档案。
- 仓库:
campus-navi-rs→frontend/public/navi/app.js、crates/campus-api/src/web.rs(提交49e17aa,版本戳202609021300)
本文归档于个人知识库 · 领域:CS & AI · 模块:04 Project_CS_AI/Frontend