8. 都是缓存惹的祸

185 阅读3分钟

之前我一直开发混合应用, 没怎么关注前端更新版本之后,用户端可能由于浏览器缓存或CDN缓存等原因,没能及时获取更新.

在混合开发中当app或者某个webview想打开一个前端项目时,可以获取最新的前端zip包,判断有更新拉取最新包即可.

那在h5开发中,由于是单页面,我们最终提供出去的其实是一个html,缓存策略的存在是为了在没有版本更新时,用户打开页面更快,提升体验. 所以缓存是必要的,且还要额外考虑有新版本更新时用户第一时间能够获取到!

协商缓存的利:

可以在没有版本更新时,返回304 Not Modified,直接从缓存加载html(js css 、img等直接走强缓存)

弊端:

你或许遇到过明明发布了新版本,但是浏览器还是返回304 Not Modified, 这个时候出问题的不是浏览器,可能是CDN服务器上没有更新.

我这里有几个方案:

第一种:

在页面初始化前,调用/getversion接口获取最新版本,与本地版本进行比较(可以放到标签或者storege都可),如果本地落后调用location.reload()更新, 亲测location.reload()、location.reload(true)都不能完全忽略浏览器缓存,但是可以在reload之前修改页面的URL,添加一个hash值,以便浏览器认为是一个新资源. 这种方法很暴力,会直接忽略浏览器和CDN的缓存.

看了网上的方案,其实可以不使用接口,前端在每次构建时生成一个version.json, 内保存着版本信息,然后保证客户端每次都拉取CDN上的version.json(version.json?hash=random),然后来做版本对比.这种方案不需要后端参与,但是不如API准确,毕竟前端的资源是放到CDN上,CDN的行为较难预测.

对了,打包工具在给文件生成hash值时是根据文件内容生成的,也就是说文件内容如果没有改变,重新打包发布到服务器上的文件名没有变化,即使暴力reload,index.html中没有改变的js、图片等依旧可以继续走缓存.

第二种:

这种问题的产生其实是在发布项目时,直接将index.html原封不动替换CDN上的资源了,所以导致CDN依旧使用缓存副本. 解决这种问题的方式可以在每次更新文件后,给文件名添加版本号或哈希值如index.html?hash=random().

第三种: 暴力

index.html 文件本身可以设置meta no-cache 禁止缓存.

还需要注意的是: 在部署期间,用户没有重新打开网页,这时如果存在异步加载chunk的情况:

  1. 如果chunk文件被删除, 用户有一定概率报错Uncaught ChunkLoadError: Loading chunk <CHUNK_NAME> failed.导致页面白屏, 需要用户端刷新一下才好
  2. 如果chunk文件被修改,hash改变,可能会出现1那种找不到文件的情况,也可能出现加载新的chunk,导致页面不固定类型的错误