最近项目里遇到一个挺烦人的问题,记录一下,可能也有人踩过。
背景
- 微信小程序里用
web-view打开 H5 - H5 是 Vue 的 SPA,hash 路由(
/#/) - 部署在 Nginx
- 前端打包出来的 JS 都带 hash,比如
app.8f3a9c.js
线上问题
新版本发完之后:
-
每次都有一部分用户白屏
-
华为手机用户为主,别的安卓手机也有点
-
清理微信缓存:
- 有人有用
- 有人没用
-
再过几天又“自己好了”
最难受的是:复现不了。
一开始我们以为是 JS 缓存问题
第一反应:
是不是用户缓存了旧 JS?
但很快发现不对:
- JS 文件名本身就带 hash
- Network 里看到的是 JS 直接 404
- 不是缓存旧 JS,而是 HTML 里引用的 JS 根本不存在了
后来才意识到:真正被缓存的是 index.html
关键点在这里。
1️⃣ SPA 只有一个入口
Vue SPA 本质就是:
- 所有路由都在前端
- 浏览器只请求一次
index.html
2️⃣ 小程序跳转的 URL 看着不一样,其实是一样的
https://xxx.com/xxxWeb/#/?a=1
但实际上:
#后面的内容 不会到服务器- 服务器收到的永远是:
GET /xyWeb/
👉 也就是说:所有用户访问的,永远是同一个 URL
白屏到底是怎么来的?
把链路捋清楚就很清楚了:
-
某些地方(微信 / 网关 / CDN)缓存了旧
index.html -
旧 HTML 里引用的是:
app.oldhash.js -
新版本发布后:
app.oldhash.js已经被删掉
-
浏览器加载 JS 直接 404
-
Vue 起不来
-
页面白屏
所谓“过几天自己好了”,只是缓存过期了而已。
我们试过的方案(但都不靠谱)
禁缓存 Header
add_header Cache-Control no-cache;
没啥用。
因为很多时候:
- 请求可能压根没到你这台 Nginx
- 上游缓存不一定听你的 Header
改 index.html 文件名 + try_files
这个能缓解一部分情况,但不是终极解:
- 上游如果缓存了原始 URL
- 新 HTML 根本请求不到
真正想通的一点
后来想明白一件事:
缓存层你控制不了,就别想着“管缓存”,直接绕过去
也就是说:
每次进 H5 的 URL 必须不一样
最终方案(一次改动,彻底解决)
我们最后只改了 小程序跳转 URL:
const ts = Date.now()
const url =
"https://xxx.com/xxxWeb/?v=" + ts + "#/?enterpriseCode=xxx"
关键点:
-
?v=xxx一定要放在#前面 -
因为:
?会发给服务器#不会
每次进入:
- URL 都不一样
- 所有缓存层都会当成新请求
- 强制重新拉
index.html - 新 HTML 引新 JS,直接正常
Nginx 还要不要改?
不需要复杂改动。
我们现在 Nginx 就是普通配置:
location /xxxWeb/ {
alias /usr/share/nginx/html/xxxWeb/;
}
加不加 no-cache 都无所谓了,不是核心。
总结一句话
微信小程序跳 H5 白屏,
不是 JS 缓存问题,
而是 index.html 被缓存了。在缓存不可控的环境下,
让 URL 每次都变,是唯一靠谱的解法。
这个问题折腾了挺久,因为小程序不是我们开发的,沟通成本有点高,最后确定好问题,才去协调,虽然花了一些时间最终解决,记录下来 希望能帮到还在怀疑人生的同学 😅