移动端H5本地化方案调研和实践
第一时间了解程序员大小事儿
作者:萱少,在工作中是一个性格开朗,乐于交流的程序员,在部门担任移动端和web前端的职位,主要工作包括技术攻关,技术支持,疑难问题排查等相关工作。解决问题、开发出使用简单的产品使我乐此不疲。
摘要:随着越来越多的APP采用H5加原生的混合开发模式,H5在APP中加速度的优化是势在必行,静态资源本地化方案值得去实施。
01
起源
随着越来越多的企业级应用,在功能模块开发时都采用H5和原生的混合开发。H5能够快速实现可视化图表,一端开发多端运行。而且能够快速实现APP的迭代,这些功能原生实现起来比较慢。
但是H5也存在一些缺点。比如H5体验糟糕且耗性能,而且在响应速度和无网操作都存在较大缺陷。此外不支持调用系统硬件功能(方向传感器、语音、蓝牙等功能)。
02
为啥体验糟糕
为什么webview在加载网络资源时会有长时间的白屏,在这个过程中都做了那些事呢?大概是:
1.初始换webview。
2.建立请求连接
3.下载HTML、JS、CSS等文件
4.webview解析执行
5.后端处理
6.接受数据
7.渲染界面
在上述过程中第四阶段以前都是白屏时间,但第四阶段也只是完成初始阶段
H5界面的渲染,并未完成整个HTML文档的加载,直到第七阶段结束时才算整个文档加载完成。具体请看下图:
H5界面的展示一般是在DOM节点完成渲染之后,可以得出结论,H5首屏渲染白屏的问题关键在于,如何优化请求页面到完成DOM渲染的时间。一般DOM渲染的时间取决于界面的繁杂成都和开发者的设计能力,而且可优化的空间非常小。所以优化的重点放在H5资源的请求上。
03
如何优化
查看网上的资料,具体的前端和后端的优化方案。主要是:
- 降低请求量:合并资源,减少 HTTP 请求数,minify / gzip 压缩,webP,lazyLoad。
- 加快请求速度:预解析DNS,减少域名数,并行加载,CDN 分发。
- 缓存:HTTP 协议缓存请求,离线缓存 manifest,离线数据缓存 localStorage。
- 渲染:JS/CSS优化,加载顺序,服务端渲染模板直出。
一般情况下,我们只要对照这个列表,对比差异就基本能搞定绝大部分前端性能问题了。不过仔细的再分析下,对首屏启动速度影响最大的就是网络请求,包括加载HTML、CSS、JS等静态资源。
但是具体问题具体对待,以上的方案还可以进行优化。那么将H5的页面和需要的资源打包到客户端里边,然后客户端将数据传给H5,通过webview加载展示。这样几乎不需要网络请求,webview只需要渲染界面和执行JS即可。通过对原生客户端的调研发现,H5资源本地化有两中方案。具体如下:
1.半加载模式。这种方式就是HTML资源还是加载网络,HTML文档所需的JS、Image、CSS等耗时的资源,通过客户端拦截HTTP/HTTPS请求的方式,替换加载本地的资源。此方式的虽然比较好,但是部分资源还是依赖SERVER端,不是最佳方案。此外,在iOS13系统时,已经弃用UIWebView,推荐使用在iOS8推出的WKWebview。而WKWebview不支持这种半加载模式。
2.完全加载模式。这种方式是比较优先推荐,此方式所有的资源本地化,webView只需要专注于界面渲染,所需要的数据也只需要通过JS发送网络请求获得,大大的降低webview需要加载HTML资源所耗的时间。但是这种方式有个问题,就是webview记载资源是通过file协议,而不是通过传统的HTTP请求。但这就涉及到了跨域,所以需要web站点支持跨域请求。
04
具体实现
整个架构看起非常的清晰,但是还需要一些基本的问题要解决:
本地H5如何与Native(原生app)通信的问题
基本上本地H5和native交互方式大体分为3类,包含:API互相调用、URL Scheme、以及Cordova(第三方框架,专门用来H5和Native端通信)。
1.API方式。H5和Native通过相互约定的方式,定义好对应的API,互相调用。
2.URL Scheme方式是web页面重定向一个约定好的URL,Native拦截URL,判断URL,展示不同的场景。
3.Cordova 是第三方提供的一个SDK。原理是web端调用prompt方法,Native端拦截弹框提示,根据相互约定好的格式,展示不同的场景。
开发和调试
开发和调试H5 代替原生实现的优势,一个在于开发成本低,另一个在于 H5 可以更加快捷的更新迭代,如果打包在客户端中的H5 资源就像客户端一样,没法快速更新了,那就失去了快速迭代的魅力。那么怎么解决这个问题呢?于是想到H5资源给到SERVER端,客户端按照业务模块预下载整个离线包,离线包根据版本做增量更新。这种方案,就可以较好的解决此问题。
1)数据量比较大
虽然Redis也可通过集群的方式达到很大的容量,但成本较高,所以动辄几百GB的缓存数据可以优先考虑使用SSDB。
2)对存取速度和时效性要求不高
SSDB基于磁盘的存储模式就决定了其某些数据操作的效率要比Redis差一些,而且主从间同步也可能会有延时,所以SSDB对存取速度要求高,或有存后即取场景的系统不太适用。
3)与业务逻辑耦合不强
SSDB更适合作为临时缓存层,而不是作为最终数据存储解决方案。由于SSDB对高可用支持不是很好,所以系统中应避免对SSDB缓存的强依赖,避免因SSDB宕机导致的大范围服务不可用(此规则也适用于其他缓存服务)。
05
web端技术栈选择与项目结构
目前web前端比较流行的三大框架有Vue、React、Angular。Angular是由Google在维护,拥有出色的数据双向绑能力。React是由Facebook在维护,率先提出了虚拟DOM概念,并且拥有完善的组件生态。Vue是由国内团队维护,由于比Angular和React推出的都比较晚,集数据双向绑定和虚拟DOM于一身,深得国内开发者喜爱。且Vue更注重UI层,对开发者的门槛要求较低,只需要开发者懂基础的HTML、CSS、JS 知识。
基于公司开发的web应用程序都是基于Vue技术,由Vue官方提供Vue-cli生成目录架构,并由webpack打包生成最终资源文件,发布在web站点。但是这种方式生成的资源文件是不能完成本地化加载模式。既然在已经选择了Vue技术栈,又不能Vue-cli前提下,还有那些方式实现呢?
模块化规范
此时想到了比较成熟的模块化加载方案主要有AMD 和 CMD 。CMD是同步加载规范,也称依赖加载规范。这种方式比较适合服务端环境。AMD是异步加载规范。
Asynchronous Module Definition 既异步加载模块规范。从规范描看,AMD很短也很简单,但它却完整描述了模块的定义,依赖关系,引用关系以及加载机制。从它被RequireJS,NodeJs,Dojo,JQuery使用,也可以看出它具有很大的价值,没错,JQuery也采用了AMD规范。AMD解决模块化编程带来的代码加载先后顺序问题及常规异步加载代码带来的不确定因素。
那么什么是AMD呢?作为一个规范,只需要定义其语法API,而不关心实现。AMD简单到只有一个API,既define函数:
define([module-name], [array-of-dependencies], [module-factory-or-object]);
其中:
module-name:模块标识,可以省略。
array-of-dependencies:所依赖的模块,可以省略。
module-factory-or-object:模块的实现,也可以是一个JS对象。
从中可以了解到,第一个参数和第二个参数都是可以省略的,第三个参数则是模块实现本身。
AMD规范实践
第一步引入项目依赖的库,如Vue.js、axios、vconsole、RequireJS、VantUI等。如下图所示:
第二步创建require.config.js对Require进行全局配置。具体配置如下图所示:
第三步新建入口文件index.html,并引入require.config.js。如下图所示:
第四步新建main.js将其他模块和入口文件结合起来。如下图所示:
第五步:至此已经完成项目最基本框架,如下图所示展示项目的基本目录:
综上所述,可以看到整个项目的目录和风格,是不是很熟悉?是不是已经和Vue官方所提供的Vue-cli工具生成的项目结构非常相似?没错,这就是最终的目的,让已有的Vue项目能够快速的切换到这种模式。至此AMD规范初体验基V本上已经完成,至于具体功能的实现仁者见仁智者见智,在遵从开发规范的前提下自由发挥。
那么最终的效果如何呢?经过最终测试发现,在抛开客观网络环境因素的影响下,此方案的开屏速度与原生相差不多,很好的完成预计目标。
END