5.1 KiB
5.1 KiB
首屏 CDN 回退与加载自愈经验总结
更新日期:2026-04-22
现象
- 首屏 loading 会卡在
54%、58%一类中间值不再结束 - 实际上 HTML 已经返回,部分首屏资源也已就绪,但启动完成条件仍未满足
- 某些静态资源本应优先走 CDN,结果请求落到了源站
- 更糟时,回退链路会拼出错误的 CDN 地址,例如:
https://cdn.jsdelivr.net/npm/aitu-app@0.6.53/npm/aitu-app@0.6.53/assets/tool-windows-Cuh0TRgJ.css
这种地址必然失败,随后页面会长时间停在半启动状态。
根因
1. 路径归一化没有在所有入口统一收口
这轮最核心的问题不是“首屏 CDN 绝对链接没写上”,而是不同阶段对资源路径的理解不一致:
- HTML 启动阶段会生成一轮 CDN 地址
Service Worker回退阶段会再生成一轮 CDN 地址- 但两边对输入路径的清洗规则不一致
之前只处理了:
/aitu-app@版本/assets/...
没有完整处理:
/npm/aitu-app@版本/assets/...npm/aitu-app@版本/assets/...- 完整 jsDelivr 绝对 URL
结果就是已经带版本前缀的路径,被再次拼接了一次版本前缀,形成双前缀坏链路。
2. loading 完成条件绑得太重
这轮另一个体验问题是:首屏 loading 把“应用可展示”和“更多后台资源是否全部准备完”混在了一起。
实际用户需要的是:
- 首屏壳层可见
- 首屏核心 JS/CSS 可执行
- 关键初始化已完成
不需要的是:
- 所有预热资源
- 所有次级页面资源
- 所有后台预取任务
如果把这些都算进首屏完成条件,进度条就会卡在中间值,看起来像“坏了”,其实是在等不该等的东西。
这轮修复里最值得沉淀的做法
1. CDN 路径标准化必须单点收口
无论资源最初来自哪里,进入“生成 CDN URL”前都必须先做同一套标准化:
- 已经是绝对 URL:直接识别,不重复拼接
- 带
npm/aitu-app@版本/前缀:先剥离 - 带
aitu-app@版本/前缀:先剥离 - 最终只保留标准
assets/...相对路径,再统一生成 CDN 地址
经验:
- 不要在多个阶段各自“猜一次路径”
- 不要假设上游传来的路径一定干净
- 路径归一化不是小工具函数,而是启动链路的正确性边界
2. HTML 入口和 SW 回退要共享同一套脏输入认知
这轮修复说明一个规律:
- 只修 HTML 入口,不够
- 只修
Service Worker,也不够
因为真实流量里,同一个资源可能经历:
- HTML 内联启动器计算首选地址
- 浏览器发起请求
- SW 拦截并尝试 CDN / 源站回退
只要其中一个阶段对脏路径处理不一致,最终就还是会出现坏链路。
经验:
- 入口负责“生成正确首选地址”
- SW 负责“对所有输入做最终兜底清洗”
- 两层都要有保护,不能只赌一层
3. 首屏 loading 只等首屏,不等预热
这轮现象已经证明:
- 预热资源可以慢
- 后台预取可以晚一点
- 但首屏完成感不能被它们绑住
经验:
- 首屏 loading 的完成条件要收敛到“用户已经能正常看到并开始交互”
- 预热应该放后台推进,只影响后续体验,不阻塞当前启动闭环
- 进度条一旦进入后半段,必须只受首屏关键依赖控制
一句话:预热是加速器,不是门禁。
4. fallback 不只是“再试一次”,而是“保证可自愈”
这次暴露出来的问题是:
- CDN 第一次失败
- 源站后来成功返回
- 但页面仍停在
58%
说明 fallback 不能只关注“网络请求最终有没有成功”,还要关注:
- 启动状态有没有被正确推进
- 坏链路失败后,后续正确链路是否真的接管了页面
- 必要时是否要通过一次 reload 进入干净状态
经验:
- 回退成功后,要么显式推进启动完成
- 要么在确认首屏资源已就绪后做一次可控自愈
- 不能让页面停在“资源已经够了,但状态机没走完”的中间态
这轮应该固化成默认规则的检查项
- 任何生成 CDN 地址的地方,都先走统一归一化
- 测试用例必须覆盖脏输入,而不只是标准
assets/... - 首屏完成条件只包含首屏核心资源,不包含预热任务
- 预热失败不能阻塞页面可见
- 回退成功后要验证“页面状态”也已经恢复,而不只是“请求成功了”
推荐的测试样例
assets/index-xxx.js/assets/index-xxx.jsaitu-app@0.6.53/assets/index-xxx.js/aitu-app@0.6.53/assets/index-xxx.jsnpm/aitu-app@0.6.53/assets/index-xxx.js/npm/aitu-app@0.6.53/assets/index-xxx.jshttps://cdn.jsdelivr.net/npm/aitu-app@0.6.53/assets/index-xxx.js
这些样例都应该被归一到同一个最终资源路径。
一句话总结
这轮最值钱的经验不是“把一个错 URL 修掉了”,而是明确了两条规则:
- CDN 回退链路里,路径标准化必须统一收口
- 首屏 loading 只能等待首屏关键资源,不能等待后台预热
只要这两条不破,后面再做 CDN 优先、回退、自愈,系统才会又快又稳。 🚦