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.htmlno-storeWebView 整页缓存会坏
// 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。

可迁移的通用判断

  1. "页面渲染好了但 X 功能慢"优先查启动串行链:确认 X 是否被某个重资源(尤其第三方 SDK 主包)在调用时序上堵住。
  2. 识别不依赖 SDK 的能力:学校桥定位 + 后端逆地理其实不碰 BMapGL,只是代码把定位排在 mapReady 之后。把不依赖重依赖的请求提前并行,是通用的启动优化手法。
  3. 版本戳驱动的静态资源就该 immutable:URL 带内容版本 → 长缓存零风险;"每次回源校验"是保守但不是最优。
  4. 文档查不到的 SDK 参数,扒官方发布的压缩源码:noAnimation 就是如此发现的(见关联笔记)。

关联


本文归档于个人知识库 · 领域:CS & AI · 模块:04 Project_CS_AI/Frontend