先说明,此处所讲的混合APP,就是hybrid APP,意思是APP中并非全部都是原生页面,而是原生与H5并存。甚至于有些混合APP中基本都是H5页面,APP仅作为一个壳,用行话来讲的话叫套壳APP,其作用是抢占用户手机上的一个应用入口,提高用户黏度,拉近用户与H5的距离——毕竟每次都要先打开浏览器再输入地址回车或者从浏览器收藏夹里点开页面的过程就跟这句话念起来的感觉一样——烦琐。这篇文章里我将谈一下如何处理混合APP中H5页面的缓存。此处可以有点掌声(自嘲状)。
在提出解决方案之前,我们先要明确一个前提,给APP加功能的时候应当尽可能的灵活些,不要轻易写死某些功能,因为APP一旦发版本上线,就算后续版本中修复了历史版本中的某些bug,但用户手机上装的APP不一定都是最新版本,这会导致某些bug长期存在。有一些bug,可以通过H5这边写脏代码做hack处理,但有些bug是没办法处理的——所以APP加新功能后一定要仔细测试,因为它没法像H5那么灵活。
ok,现在言归正传谈一下解决方案。
目录
一、方案描述
配置H5链接的时候在链接的search部分添加一个可选的refresh=1参数。当APP打开某个H5链接时判断url上是否有配置refresh=1:
1、如果url上没有配置refresh参数或者配置的值不是1,则APP直接走默认处理,这个默认处理一般是根据服务端(含CDN)的响应头里的过期时间来处理。
2、如果url上有配置refresh=1,则APP自动在链接的search部分添加一个动态的参数值,如ts=1586147745670,这里的参数值可以是时间戳。
二、方案解读
有人可能会问,既然可以直接根据响应头的过期时间来自动处理,为啥还要另外多此一举?如我服务端响应头里告诉浏览器说过期时间是1天,那么这1天里浏览器可能就不重复向服务器请求对应资源了,但是这个时候如果发现了产线bug就是需要更新资源呢?另外对于使用了CDN的情况,公司服务器下发响应头的更新到CDN更新在时间点上通常也不是完全同步的,而且一般公司用的都是外部CDN,那么沟通成本也是难免的。
有人可会问,这个方案貌似只是针对html文件的。对的,没错。因为只要html文件的缓存问题可控,html里面所引用的资源的缓存问题就完全可以由H5这边随意控制的,只要对需要更新的资源的引用地址进行修改即可(修改文件名或者添加版本号之类的),目前H5流行的webpack构建根据可以根据文件内容来生成contentHash,这样只要内容有变化才会改变该值,不需要人为手动控制。
可以说,本文中提到的这个方案不但效果拔群,而且成本小,对现有页面也不会产生影响。这里需要注意的点是,APP添加动态参数值时,必须要添加到url的search中,不能添加到hash中。因为仅hash值改变对于浏览器来说还是同一个“地址”(其实location.href是变了的,这里的“地址”可以理解为缓存库中用来取缓存的key,仅改变hash不会影响key,所以仍会命中的同一个缓存内容),但是search部分改变后对于浏览器而言就是一个全新的地址key了,不存在已缓存的内容,所以会从服务端重新获取资源更新。
采用该方案时需要注意,能缓存的东西还是要尽量去缓存的,所以:
1、对于不常更新的资源,一般不用配置refresh=1。
2、对于不重要的资源更新,一般不用配置refresh=1,就慢慢等资源过期时间到了自动更新就行了。
3、如果是对不常更新的资源进行了重要的内容更新,可以临时开启refresh=1,等响应头里配置的资源过期时间到了以后再去掉refresh=1配置。
2、对于经常更新的前端资源,在配置refresh=1的同时,记得把html里的内嵌资源抽出来用单独的script或者link标签去引用资源,不然每次刷新都会有大量没更新的内容占用宽带影响资源的下载速度。一般对于单页应用而言HTML本身通常没多少东西,所以这一点主要是针对一些未对样式、脚本和html进行分离的历史项目而言的。
三、题外话:普及下url的search和hash
因为我发现不管是服务端、客户端还是H5端,很多同学根本不分url中的search和hash。公司的项目以及做的一些私活里都碰到过这样的问题,取url上的参数时不是先去区分search和hash,而是根据?和#的相对位置来处理,可读性非常不好。
举个例子,对于链接:https://www.baidu.com/?searchKey=searchVal#hashRouter?hashKey=hashVal,其各个组成部分的内容可以参考下面的图片示意。
如果还不清楚的话,可以自己在浏览器地址栏随便输入一个地址,回车后在浏览器控制台中输入location并回车来查看下location对应的内容来帮助理解。