"Best practice" WebView preloading and resource caching

As mentioned earlier, the only way to intercept WKWebView resource requests , this article will organize the entire set of preloading and caching solutions for your reference.

background

program objectives

  1. In the App, let the Web online page achieve the loading efficiency of the Web offline package .
  2. There is no need to change the current front-end development process and infrastructure.

Online and offline solutions

Briefly talk about the distinction between online solutions and offline solutions .

online program

The web container directly loads the remote URL, which is essentially the same as loading the URL on the browser. In terms of implementation, a set of JSSDK is provided to provide some limited capabilities, such as obtaining Token and obtaining current information.

Key advantages: the front-end/terminal containers are basically decoupled, and any web page that fits the vertical screen UI can be loaded in the online solution . It is also a very comfortable solution for the front-end development experience, as long as you care about the front-end itself.

Key disadvantages:

  1. The user experience is unsatisfactory. No matter how optimized it is, users can perceive that this is the way the browser loads.
  2. JSSDK can only provide limited capabilities, because there are cross-domain issues. The cross-domain issues here are not cross-domain issues between domain names, but cross-domain issues between the App container and HTTP, such as accessing App local resources from Web pages.
  3. This point is a supplement to the second limitation. JSSDK also needs various encryption security guarantees (such as the way of binding the authorization Token and URL), but it is actually useless. This is also the reason why the WeChat official account has not updated the JSSDK capability for many years. The reason, because capacity security cannot be guaranteed.

This is more suitable for the realization of non-core businesses such as information, marketing, and advertising. This is actually in line with iOS's definition of web development, non-core business. In theory, web pages cannot exceed 35% of the entire app, otherwise there is a risk of rejection.

offline solution

Web 容器加载的是 App 本地的资源,这也是小程序能成的最重要的基础(原生混合渲染那些只是体验优化,所以这里不单指微信小程序,比如冷门点的云闪付小程序,其实就是把在线 URL 打包成压缩包上传)。

关键优缺点就是在线方案反过来,用户体验更好,能用各种 App 上的底层能力,但开发体验更差,没办法直接融入前端体系。

开发体验差的主要问题在于:

  1. (重点)调试难,微信/支付宝都有自己的 IDE 开发调试工具,“基本” 可以保证在 IDE 的效果与在 App 上是一致的(坑也是多的很,特别是支付宝小程序)。
  2. 无法直接复用前端架构/生态/基建,比如 BFF 层就用不了,导致相同功能需要开发离线版、在线版,重复劳动。

在线页面离线化

列举了以上的优缺点,那可以只要两个优点都要吗?

答案肯定是可以,不然笔者在这说什么废话 ~

在降本增效的大前提下,前端开发一套 Web 页面,可以在各个容器中正常使用,且达到与离线包一样的体验,就是我们的工作目标了。

思考

如何离线化在线页面,其实能选择的方式也不多,很多大厂也会选择使用一个极小化的 WebView 内核来提前加载资源,这样的好处也很明显,App 容器上无需更多的关心缓存的实现方式。

但带来的问题就是依托 WebView 进行资源缓存,那在预加载上就必须启动一个 WebView 容器,这一点其实有性能损耗的,如果有多个页面需要预缓存,那就必须开多个容器或者提供一个预加载队列,控制起来也是尤为费事,特别在预加载完之前用户就进入页面的情景下,缓存意外也是有可能的。

那对我们来说,在放弃了使用极小化的 WebView 内核进行资源缓存的方案后,能选择的就是提前把页面上的资源下载到 App 本地,然后通过请求拦截的方式进行本地资源加载。

方案

在之前的方案上,是通过直接下载 HTML,然后通过正则的方式解析其中的 CSS、JS 文件,进行提前预加载。

但这会有一个问题,那就是不能去缓存 HTML 文件,不然会导致更多的问题:

问题1:  缓存更新不及时。

虽然每次启动后都会预加载最新的页面,但如果预加载未完成且前端有更新,用户还是会返回到旧的 HTML。

这一点可以使用严格模式,在预加载之前,先清理掉缓存,访问的时候如果没有缓存,页面会按原始情况加载,不再使用缓存加速。

那对于用户来说每次重启 App 后都会拉取最新的。

问题2: 服务器抖动等情况下,下载到不正确的页面。

这一点是完全有可能的,毕竟我们是把页面当作资源下载下来,并没有经过 WebView 解析,所以我们要检测下载的内容是否正确。


而如果没有对 HTML 进行缓存,相当于离线化方案少了一条腿,并不能彻底的达到我们想要的加载秒开率。

那在优化后的方案是这样:

image.png

预加载

从上图中可以看到,我们在 Web 中增加了一个 manifest 的清单文件,它是一个 JSON 格式文件,里面描述了:

  • indexMD5 HTML 内容校验标识
  • indexURL HTML 访问地址
  • assets 资源列表

那在 App 上,预加载要做的事就变成:

  1. 通过 get 请求,拿到 mainfast.json
  2. 根据indexURL下载 HTML 内容
  3. 根据indexMD5校验下载内容是否正确
  4. 根据assets下载资源列表

职责更清晰了,且稳定性变的更高。

而预加载的时机上:

  • 在首页加载后3秒执行,尽量不影响首页加载效率。
  • 在用户从后台返回到前台时执行。

缓存

整体缓存分为几个部分:

  1. 清单和路由的关系缓存,这个采用 key-value 的方式持久化存储,key是清单地址,value是对应的路由列表。
  2. 清单对应的 HTML,也是采用 key-value 的方式持久化存储,key是清单地址,value是 HTML 内容。
  3. 清单对应的资源文件,采用本地文件映射的方式,把资源文件一一映射成本地路径,所以可以直接判断文件是否存在,不存在再下载,提高缓存性能。

image.png

而这还要注意处理的是缓存冗余,对于前2种 KV 缓存来说,缓存并不会产生冗余。但对于资源来说,会随着前端发包而变得越来越多,这时候就需要清理掉这部分过时资源。

清理手段也很简单,分为2步:

  1. 获取最新的 manifest,对assets进行差集对比,标记出已过时的资源。
  2. App 重新启动后,清理过时资源。

这样保证在保证预下载过程中,用户打开网页不会产生资源丢失等异常情况。

image.png

稳定性

安全第一一套新方案落地前,需要有完备的监控手段以及可快速降级手段,来避免上线风险。

而对缓存及预加载的要求,就是一旦发生异常情况,及时抛弃缓存,直接加载页面。这在有了清单文件后,变得十分可行:

  1. mainfast 是 JSON 数据,当返回异常场景下,读取 JSON 失败,则放弃本次缓存。
  2. 下载的 HTML 校验 md5 失败,则放弃本次缓存。
  3. 增加白屏监测,出现白屏现象则及时放弃缓存并刷新页面。
  4. 灰度出现问题反馈,及时关闭功能开关,降级为之前的实现方式。

总之就是一有异常就丢弃缓存,一有反馈及时停止实验。

资源访问

通过请求拦截的方式,具体见:唯一一种拦截 WKWebView 资源请求的方式

后续

这只是整套 Web 容器方案的一小部分,但笔者觉得也是值得拿出来分享一下,新 Web 容器会在近期灰度上线测试,到时候还有什么坑笔者也会持续分享 ~


感谢阅读,如果对你有用请点个赞 ❤️

Mid-Autumn Festival GIF animation guide is watching tips.gif

本文正在参加「金石计划」

Guess you like

Origin juejin.im/post/7223653286465273911