1 背景和需求
原公司为客户部署了一套 OA
系统,系统的移动端分别采用了安卓和 IOS
原生开发。移动端的首页采用webview
内嵌的方式打开了一个 H5
网页,该网页的主要功能是展示新闻列表和作为其它应用的入口。整套系统的使用人数会在10w+,且使用者网络环境复杂,会面临无网或者弱网的情况。项目前期已经做了网络传输以及骨架屏方面的优化,但仍无法达到客户的要求。
对于用户而言,核心的需求有两个:第一是无网环境下依然能够访问网页,不至于打开应用后显示错误或者空白;第二是在弱网环境下加载速度要快,不能长时间的显示空白或者 Loading
。为了方便讲解核心思想和方案,将原有业务进行 了抽象,可以简单的理解为下图所示的网页界面。网页的业务主要包含以下几点:
- 包含应用入口和文章列表模块,并且用户可以配置显示哪些模块。
- 服务端对数据获取提供了两个接口,分别是获取用户配置数据接口和获取配置对应的数据的接口。
- 整个页面支持下拉刷新,下拉刷新时根据当前配置重新获取文章及应用入口。
- 文章图片的
url
有有效期,过期之后再次访问 url
无法获取图片。
2 实现思路
2.1 概述
要使应用首页能在无网状态下依然被正常访问,需要对H5应用进行本地化处理。主题思路是充分的利用缓存,将必要的内容缓存,以便在无网或者弱网情况下利用缓存加速页面渲染,优化用户体验。而缓存主要包括两部分内容:一是资源的缓存,二是数据的缓存。源缓存采用缓存优先的策略,即如果有对应资源的缓存,则使用缓存数据,如果没有对应资源的环境,则从服务端拉取资源缓存并返回。数据缓存则主要是缓存接口数据,数据以 key-value
的形式保存在本地。
2.2 资源缓存
2.2.1 HTML
首页应用为单页面应用,html
会作为应用的入口文件。在服务器上部署门户应用后,入口地址一般不会发生变化。因此客户端在缓存优先的基础上,需要在后台完成 html
文件的更新。
2.2.2 其它静态资源
与 html
资源不同的是,对于其他静态资源,无需更新缓存。这是由于在前端打包的时候,会使用文件的hash
作为文件名的一部分,因此文件内容改变,请求的 url
一定会变。如果 url
没发生变化,客户端可以认为,资源没有变化,直接使用缓存即可。
注意:对于直接引入
public
中的资源,由于打包的时候一般不会生成 hash
值,建议在引入时添加 ?v=1
版本号参数,从而确保 url
可以唯一标识某一资源。
2.2.3 动态资源
动态资源是指,由用户上传的资源。由于这类资源对安全性要求较高,因此需要对这类资源的访问做了权限校验,目前的校验是通过在资源的 url
上拼接授权码来实现的。用户每次进入应用,获取的授权码可能是不一样的,导致资源 url
发生变化。这样使用 url
作为 key
来缓存动态意义不大,不利于弱网环境的优化资源加载体验,也会导致客户端相同资源多次缓存。因此需要客户端将url上的授权码删除后作为 key
来缓存和查询动态资源。
2.3 数据缓存
业务数据使用 key-value
的形式存储,key
为字符串,value
为 JSON
对象。需要支持以下的方法:
- getItem(key: string):根据
key
获取对应的数据。 - setItem(key: string, value: object):根据key更新或者新增对应的
JSON
数据,返回是否修改成功。 - removeItem(key: string):删除
key
和 key
对应的 JSON
数据,返回是否删除成功。 - clear():删除所有的数据,返回是否清空成功。
3 技术方案
3.1 基于客户端
本身 webview
是内嵌在客户端内的,网页所有的请求客户端都可以拦截到。客户端可以将指定的资源缓存到端上并在端上的数据库构建资源的URL和资源存储地址的映射关系。最终通过在客户端侧编写相应的缓存逻辑,实现2.2章节的资源缓存效果。客户端有自己的SQLite数据库,并通过 JSSDK
跟 H5
做数据的交互。因此对于数据缓存而言,只要 JSSDK
实现2.3中的接口即可。
3.2 基于H5
基于H5的本地化选项并不多,比较主流的借助 Service workers
技术在 WEB
应用程序和后端服务之间建立代理服务器,并通过 CacheStorage
来实现资源的缓存和分发。通过对资源缓存的 worker
的代码的编写,可以实现2.2章节的资源缓存效果。对于数据存储而言,浏览器的 localStorage
本身完美支持2.3的数据存储接口,但是考虑到考虑到可能出现的大数据量的存储,最终选用了 indexedDb
用于数据的存储,并实现了一套2.3章节的接口。
3.3 对比
优点 | 缺点 | |
基于客户端 |
|
|
基于H5 |
|
|
4 业务实现
4.1 原有业务流程
在没有接入本地化方案之前,页面渲染流程相对来说比较简单,在资源加载完成后只需要依次的调用页面配置接口和页面数据接口,然后根据两个接口的数据渲染显示页面即可。
4.2 新的业务流程
4.2.1 获取页面配置
考虑到页面布局及配置信息变化频率不大,该接口数据采用缓存优先的策略。前端会优先使用缓存数据来渲染页面并进行下一步的业务处理,同时会在后台异步的请求服务端数据,在服务端数据返回后,缓存和服务端数据的更新时间是否一致,如果不一致则触发重新渲染。有网且有缓存情况下产品表现为:管理员修改页面配置后,用户第一次进入应用会显示之前布局骨架并加载数据,然后页面显示新的布局骨架并重新加载数据。具体缓存流程如下图所示:
4.2.2 页面数据
为了让用户更快的看到内容不至于白屏,该接口也采用缓存优先的方案。但由于文章的更新较为频繁,因此在接口数据的请求过程中,会在页面顶部展示数据加载中的效果,直到请求结束。有网有缓存的情况下产品表现为:用户先看到历史缓存的数据,页面顶部显示数据加载中的
Loading
提示,此时用户可以正常查看详情,上下滚动,但无法再次触发数据刷新。在接口请求完成后,会再次触发个功能模块的刷新,用户看到最新的数据。具体缓存流程如下图所示:
5 总结
整个本地化方案的调研主要是从 H5
和客户端两方面入手,且分别使用这两种方案对项目进行了本地化的改造。两种方案都能实现预期的目标,首页的加载速度在各种网络环境下基本可以稳定在700ms左右。不过最终给客户上的是基于客户端的方案,主要原因是对兼容性和考虑和未来可能的本地缓存加密需求。