1. 缘起
说起webview与h5,对整个移动开发业界的人来讲,都是又爱又恨。爱其功能强大、可动态发布、又多端复用。。。又恨其加载慢、响应慢、体验不佳。慢、慢、慢的问题,往往让开发挠头、让客户骂娘!天使与恶魔的共同体,好的坏的都那么明显。
业务快速迭代,h5页面往往承担着一家公司的大部分流量来源,这对有赞也不例外。优化h5的加载,提升客户体验,收益和价值都是巨大的。
2. 怎样做webview/h5加速?
要提升h5的加载体验,首先要弄清楚的是,到底是什么影响了h5的打开速度。这是一个系统性的问题,涉及到移动端、前端、后端多个领域、整条链接的加载优化。本文将只聚焦于从移动端的角度怎样做优化。
2.1 从WebView.loadUrl(url)
讲起
当我们开始加载一个网页,从调用WebView.loadUrl(url)
开始,系统到底做了哪些事情,各个阶段的消耗情况如何?
从移动端的角度来看,一个网页的加载过程总体分为上图所示的几个步骤,每个步骤消耗的时间累积影响整体的加载速度。
我们对于有赞的的一些典型h5页面做了各阶段的测试(在wifi条件下),总结如下表:
阶段 | 耗时 | 说明 |
---|---|---|
webview初始化 | 约100ms~250ms | 首次需加载webview内核耗时 |
dns解析+tcp连接 | 20ms~1.5s | 波动较大,最差的情况遇到过1.5s的case |
html下载 | 约200ms~600ms | 多数情况400ms左右(wifi) |
html解析 | 100ms~150ms | |
静态资源下载 | 50~200ms | 此为同步资源消耗,异步资源未计 |
2.2 到底什么更影响加载体验?——白屏时间 or 整体加载时间
从直观的角度讲,当打开一个h5页面的时候,页面会有较长时间的白屏——此所谓响应慢。反应到上图的系统过程中,它包括:webview内核初始化(首次打开)、DNS解析及建连、请求html并下载、解析html及同步资源这几步。我们把这个过程记为白屏时间。白屏时间在h5页面加载的前半程,对整体的体验影响最大。
而页面的整体加载时间,还包括异步资源、数据请求的完成,往往是在当前页面已经可看、可操作的后台继续进行着。对用户的体验影响较小(除非首屏有大块区域需要这种异步资源或者数据请求作展示,比如:有赞的商品详情页顶部大部分是商品轮播图)。
从上述表格来看,页面整体的加载过程在1.2s左右,白屏时间至少400ms~800ms。这是在wifi条件下测得的数据,换成移动网络2G/3G,情况会更糟!
2.3 从哪些方面着手?
清楚了各个阶段的消耗,以及对体验影响最大的白屏时间。从以下方面入手:
-
webview内核的加载
这里指webkit内核的初次加载,包含java类的load、动态库查找load到内存中,也包括webview的创建。可考虑的一个做法是,应用启动时全局加载webview内核,以空间换时间(此法大约有10M左右的内存占用)。
-
DNS解析优化
DNS解析情况好的时候,几毫秒、十几毫秒就完成了。差的时候,可能需要花很多时间(遇到过需要1.5s的case)。域名收敛、对DNS做prefetch, 尤其对优化一些长尾case非常有用。
-
静态资源缓存
如果对css/js、图片、或者其他如ttf等静态资源,预先缓存到本地,可以省去从远端拉取静态资源的消耗。在我们的解决方案中,对于初次、二次打开页面,静态资源的命中率都可以做到80%~100%。
但是必须清楚,除了那几个解析渲染首屏html必要的、同步的静态资源(css/js), 提升静态资源的命中率整体对于白屏时间的优化是比较有限的。静态资源的加载大多是在页面加载的后半程。
-
html预取及缓存
从上面数据可以看出,下载html内容的消耗在400ms左右,这是白屏时间的一大来源。如果能缩减这部分时间,则可以大大减少白屏。将html内容提前下载到本地,
WebView.loadUrl(ur)
时直接从本地加载,速度要快得多。但html本身是常变化的,如果本地缓存的html内容过期了怎么办?
如果html的内容是用户相关、需要登录才能看的怎么办?
不过,html的预取、缓存虽然有诸多限制,但并非没有适用的场景!
在有赞的webview加速解决方案中,通过对可缓存的html范围做限制,解决了店铺首页、活动页的h5打开提速问题。这些页面都是属于变化较少、并不私有的页面。
更重要的是:这些页面,往往都是webview的入口页(从native到webview),是最可能产生白屏的地方。进入这些页面后再点击跳转到其他页面属于webview内跳转,其体验尚可。
3. 有赞前端体系及h5资源分析
3.1 静态资源区分
有赞h5页面中的资源整体要分成两种:
一种是有赞统一资源,这指的是页面共有的css/js等资源,属于有赞的页面都可能会使用;
另一种商家特有的差异资源,只有在这个商家的店铺当中才会用到,这部分主要是商家上传的商品图片。
类型 | 路径 | 说明 |
---|---|---|
有赞统一资源 | /v2/wscwap/build/css/showcase/goods_c84308e66177da54cc8371e5d9f65767.css 、 /v2/build/wap/ump/send_coupon_f4f485ecf5.js | |
商家差异资源 | /upload_files/2017/12/14/FlF05IkscTy9TACqGg129U8Kqxex.jpg?imageView2/2/w/980/h/980/q/75/format/webp |
举几个实例:
类型 | 路径 | 说明 |
---|---|---|
有赞统一资源 | /v2/wscwap/build/css/showcase/goods_c84308e66177da54cc8371e5d9f65767.css 、 /v2/build/wap/ump/send_coupon_f4f485ecf5.js | |
商家差异资源 | /upload_files/2017/12/14/FlF05IkscTy9TACqGg129U8Kqxex.jpg?imageView2/2/w/980/h/980/q/75/format/webp |
假如有赞的两个商家A和B,分别有自己的店铺或客户端。商家A和B都会共同用到有赞的一些基础css/js静态资源。但A、B各自的商品图片等静态资源是相互隔离的。
除了共用的基础css/js等静态资源,在A的端中只能缓存自己的图片资源,B的端也同样只需缓存B自己的图片资源。
这里有必要解释一下“商家的端”的概念及具体形式:拿有赞典型的两个产品来说,一个是微商城,一个是App开店。在微商城客户端中,商家A和B分别用自己的帐号登录、管理自己的店铺,这里,商家的端=微商城客户端+A/B的店铺。
而在App开店中,有赞提供一个基础的AppSDK,包装有赞的商品系统、营销系统等后台服务,商家A和B在自有的客户端App中集成这个基础SDK,就可以使用有赞的服务。这里,商家的端=商家A/B自有的app+AppSDK
不同的商家的端,所需要的资源是不相同且必需隔离的。这一特点会影响到我们后面的系统设计。
3.2 静态资源变化与路径
做静态资源缓存的一大要点是如何解决资源变化后的实时更新问题?以及系统如何自动发现资源已经变化?变化后如何更新缓存?
我们的前端每天都在发布,每天都有一定数量的css/js等需要更新。
我们的商家们每天都在做生意、上传新的商品,每天都在添加新的图片。
对于css/js等一类资源,我们采用webpack
打包,资源内容的变化就一定会引起资源路径变化。比如:/v2/build/wap/ump/send_coupon_f4f485ecf5.js
这个资源,它的后缀部分f4f485ecf5
其实是这个资源文件的md5值前10位。
对于图片类的资源,每次上传图片,它也会有一个唯一的路径。比如:/upload_files/2017/12/14/FlF05IkscTy9TACqGg129U8Kqxex.jpg
。
变化后的资源 => 不同的路径, 这个特点对于解决如何发现变化、缓存实时更新的问题真是有太多的方便!
3.3 html页面特点及区分
有赞的html页面有很多种,各有特点:有的变化很快,有的属于隐私页面。但也有一些相对稳定的入口页、活动页。
html的加速,除了从html获取的全链接下手(包括服务器、业务方、协议层等),单就做html缓存预取来说,其实是相当难做的。因为实时性的问题,难以解决。尤其有赞的html页面并未做前后端分离。它的所有页面内容,包括一部分页面数据都是在服务器端中拼接直出的。
从之前的数据分析,html内容的获取是占白屏时间的大头。所以对它做优化是项目是否取得显著成效的关键。但是如果把前后端的同学聚集起来做大幅度的改造:前后端分离、优化协议。。。恐怕长期也难见成效。业务的发展和成长是第一位的。
如果我们把要优化html页面的范围缩小,聚焦于那些相对稳定的微页面/店铺首页、商品详情页、活动页,我们可以牺牲一点点的实时性,来换取加载体验极大提升。而恰恰这些页面往往是webview的入口页,也是访问最多的页面。对它们的加载做优化,可以得到最佳的投入收益比。
4. 金翅(goldwing)——有赞移动端h5加速平台
初看这个名字可能会觉得很怪,它很中国化,英文代号也很拉风。金翅是一种常见的鸟,我们寄寓这套系统能给我们的app带上翅膀。
金翅是我们对有赞移动端h5加速解决方案的总称,它包括Android/iOS客户端sdk、java后台服务、还有nodejs写的管理控制台。
在这套系统中,我们解决了以下几个问题:
- 有赞所有静态资源的缓存,包括统一的css/js等资源,及商家端特有的图片等资源;
- 店铺首页/微页面、商品详情页、活动页等入口页的html加速;
- 动态化配置、在线接入及管理。
在不约束下载条件(如:wifi)的情况下,不论是首次、二次以后打开页面,静态资源的命中率达到了80%~100%。
对于html的加速优化前后,请看如下动图对比:
- 不使用优化
- 使用优化后(静态资源缓存+html缓存预取)
效果还是还当的明显的!未使用优化的第一张图,可明显看出有较长时间的白屏效果。而使用优化后的第二张图,效果已经可以近于native化的加载体验了。
关于金翅如何做静态资源的缓存?如何做html的加速、缓存预取?后文将单独做专题讲述。
5. 结语
做这套h5加速解决方案,前后时间大概有三个月之久。这期间小组内同事们除了要继续做手头的业务,其他大部分的精力都投入了这项工作当中。
这期间我们参考了业内许多其他公司同仁的分析、思考与总结,结合有赞业务的特点完成了这套h5加速解决方案。
目前这套体系稳定的接受着千万级流量的洗礼,我们同时也在公司内的更多业务线中推广铺开。