微信小程序跳 H5 偶发白屏,我们折腾了很久才真正解决

66 阅读3分钟

最近项目里遇到一个挺烦人的问题,记录一下,可能也有人踩过。

背景

  • 微信小程序里用 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


白屏到底是怎么来的?

把链路捋清楚就很清楚了:

  1. 某些地方(微信 / 网关 / CDN)缓存了旧 index.html

  2. 旧 HTML 里引用的是:

    app.oldhash.js
    
  3. 新版本发布后:

    • app.oldhash.js 已经被删掉
  4. 浏览器加载 JS 直接 404

  5. Vue 起不来

  6. 页面白屏

所谓“过几天自己好了”,只是缓存过期了而已。


我们试过的方案(但都不靠谱)

禁缓存 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 每次都变,是唯一靠谱的解法。

这个问题折腾了挺久,因为小程序不是我们开发的,沟通成本有点高,最后确定好问题,才去协调,虽然花了一些时间最终解决,记录下来 希望能帮到还在怀疑人生的同学 😅