公司的业务主要是基于APP的产品,而为了达到快速迭代上线,主要还是使用了Hybrid的方式,但是相比原生而言,使用webview
+H5
还是存在一些被诟病的问题,那就是性能和体验达不到原生的程度,虽然说随着移动设备性能的增强和5G的到来,一些问题可以得到缓解。
H5存在的一些问题:
-
页面启动白屏时间长:随着MVVM框架的发展,目前大部分的Web应用都是SPA应用,在渲染页面之前还需要进行数据资源请求
-
交互响应不流畅:基于webview的内核,渲染机制和交互API都无法达到系统级
对于特效动画不多的应用,交互体验上的影响并不算大,主要的问题还是用户从打开页面到可交互的时间。
为什么H5页面的启动时间长?尤其是SPA应用?
从用户点击到页面渲染完成,大体上经历了一下过程:
初始化webview => 请求html => 解析html => 请求css和js => 渲染 => 解析和执行js => 请求数据 => 渲染 => 下载图片 => 渲染 => 页面显示可交互
常规SPA应用中,通常在请求完并且执行js时,才会出现loading
之类的画面,而在这之前都是白屏,用户体验就很差,尤其是网络慢的时候。那么怎么才能进行优化呢?
从整个过程中,可以看到涉及到了客户端、前端和后端,除开后端接口和服务器的优化,接近用户侧的客户端和前端是我们的优化空间。
在过去PC和手机浏览器中,已经有无数的优化手段,总结起来无外乎以下几点:
- 减少请求数量:合并资源,减少HTTP请求,懒加载,内联资源等
- 减少请求资源大小:压缩资源,gzip,webp图片,减少cookie等
- 提高请求速度:DNS预解析,资源预加载,CDN加速等
- 缓存:浏览器缓存,manifest缓存等
- 渲染:css、js加载顺序,同构直出等
几乎所有的应用最先遇到的性能瓶颈都是网络请求,所以能减少请求的缓存就显得比较重要。
浏览器的缓存机制这里就不做赘述,主要有通过设置Cache-Control
等HTTP请求头的方式和PWA中利用Service Worker的方式。利用浏览器缓存机制,页面再次请求对应资源时,可以从缓存中获取,减少网络请求,而数据方面的请求,可以通过localStorage进行数据的缓存,首次渲染时可以使用本地缓存数据,然后再请求更新。
通过以上手段可以使页面第二次被访问时快速打开,但是第一次访问时,依旧存在慢的问题。
首次访问需要加载js资源,然后再发起请求,等数据返回渲染后,用户才能看到。在上古年代,页面基本上都是通过服务端渲染的,页面返回时就已经带上了数据,减少了再次数据请求的过程。借助于nodejs,client端和server端使用同一套代码,可以通过node层进行数据和模版的组装,返回首屏的内容到浏览器渲染,达到“直出”,而首屏外的内容,可以用原来的方式,请求到js资源后再进行渲染,这样可以提高首屏的速度。
此时,页面要渲染,除了数据,还需要等待样式文件的加载,那么这一块可以优化吗?
同构直出可以看作是数据内联到页面中,那么作为首屏的样式资源,其实也可以通过构建打包的流程,将其内联到页面中,那么当请求页面时,服务器返回的页面,就已经包含了首屏所需要的样式和数据,用户第一时间就可以看到页面,首屏外的样式资源,可以用原来的方式再去请求渲染。
此时,对于首屏,页面打开的流程缩减为:
初始化webview => 请求html => 解析首屏html、css => 渲染 => 下载图片 => 渲染 => 页面显示可交互
webview在启动前需要进行初始化,那么我们其实可以在webview加载页面之前就将它初始化完成,将其放入缓存池,之后直接从缓存池中获取预创建的webview,而非等到用户打开页面时再进行初始化。
之前的优化方式都是放在前端,数据源直接来源于服务端,在App中,我们是不是可以从APP中获取数据资源,在webview中无需任何请求,直接就是组装渲染?
对于非个性化页面,即所有用户所见相同的页面,每个版本用户所见内容相同,那么这一块内容其实可以打成一个资源包,这个资源包包含html、css、图片和js,客户端在某个时间点可以去预先请求这个资源包,之后用户打开这个页面时,客户端拦截对应的请求地址,从本地资源包找到对应的文件返回,同时客户端提供数据源,html获取到资源和数据后进行组装渲染。
离线包的方案涉及以下几个问题:
-
前端工程的打包构建流程:增量、全量打包,公共资源包
-
更新机制:增量更新和合并,全量更新
-
安全:离线包下发版本校验,防止篡改
-
客户端的资源拦截机制
-
回退方案:离线包下载失败,回退到同构直出方案
通过离线包的方案,在用户打开页面过程中,可以做到几乎无请求,达到秒开的效果。当然,结合几种方案,必然对前端工程的打包构建影响较大,需要设计好整个完成的打包构建方案。
整体的优化需要从客户端、前端到后端一起进行,每个环节都进行了优化的话,基本就能达到秒开的性能。总结起来,大致的优化思路就是:
-
预加载和创建:能提前做好准备的都提前做好准备
-
缓存:能缓存的都进行缓存
-
并行:能同时进行的尽量同时进行
当然有些优化方法还需要视业务场景而定,比如跟用户强关联的个性化模块就不好做离线包。