VasSonic是什么
VasSonic是一个轻量级的高性能的Hybrid框架,专注于提升页面首屏加载速度,完美支持静态直出页面和动态直出页面,兼容离线包等方案。
该框架使用终端应用层原生传输通道取代系统浏览器内核自身资源传输通道来请求页面主资源,在移动终端初始化的同时并行请求页面主资源并做到流式拦截,减少传统方案上终端初始化耗时长导致页面主资源发起请求时机慢或传统并行方案下必须等待主资源完成下载才能交给内核加载的影响。
通过客户端和服务器端双方遵守Sonic格式规范(通过在html内增加注释代码区分模板和数据),该框架做到智能地对页面内容进行动态缓存和增量更新,减少对网络的依赖和数据传输的大小,大大提升H5页面的加载速度,让H5页面的体验更加接近原生,提升用户体验及用户留存率。
VasSonic依赖于三端的配合,如果由于前端、后台不支持等问题,可以考虑客户端接入VasSonic+预加载或者VasSonic+离线包组合方式,来提升页面加载速度。
VasSonic的愿景
VasSonic框架源于我们团队超过一年的优化提速总结,之所以开源给大家,一方面是希望该框架能够让更多公司外的业务跟手Q等内部应用一样受益,为用户提供更好的体验,另外一方面也是抛砖引玉,集合广大开发者的能力一起完善VasSonic或者提出下一个VasSonic,打造一个更好的Web世界!
页面规范约定
为了实现页面增量更新,节省用户流量,sonic提出将页面分割为不经常变化的模版(template)和经常变化的数据块(data)。页面将整个html通过VasSonic标签进行划分,包裹在标签中的内容为data,标签外的内容为模版。
VasSonic标签:
- <!-- sonicdiff-moduleName --> xxx <!-- sonicdiff-moduleName-end -->
上面整个模块称为一个数据块(one data),muduleName表示数据块的tag,xxx是该tag对应的数据块的内容。
- <html>
- ...
- <div id="data1Content">
- <!--sonicdiff-data1-->
- <p id="partialRefresh"></p>
- <!--sonicdiff-data1-end-->
- </div>
- <div id="data2Content">
- <!--sonicdiff-data2-->
- <p id="data2">数据块</p>
- <!--sonicdiff-data2-end-->
- </div>
- <div id = "data3">data3</div>
- ...
- </html>
举个例子,如上面代码表示是一个遵守VasSonic内容规范html,从内容中可以知道data1和data2部分是数据块(data),那么将数据块抽离之后,并通过数据块的tag进行占位,最终成为模板(template),具体如下:
<html> ... <div id="data1Content"> {data1} </div> <div id="data2Content"> {data2} </div> <div id = "data3">data3</div> ... </html>
请求规范约定
VasSonic为了支持区分客户端是否支持增量更新等能力,对头部字段进行了扩展
字段 | 说明 | 请求头(Y/N) | 响应头(Y/N) |
---|---|---|---|
accept-diff | 表示终端是否支持VasSonic模式,true为支持,否则不支持 | Y | N |
If-none-match | 本地缓存的etag,给服务端判断是否命中304 | Y | N |
etag | 页面内容的唯一标识(哈希值) | N | Y |
template-tag | 模版唯一标识(哈希值),客户端使用本地校验 或 服务端使用判断是模板有变更 | Y | Y |
template-change | 标记模版是否变更,客户端使用 | N | Y |
cache-offline | 客户端端使用,根据不同类型进行不同行为 | N | Y |
cache-offline字段说明
字段 | 说明 |
---|---|
true | 缓存到磁盘并展示返回内容 |
false | 展示返回内容,无需缓存到磁盘 |
store | 缓存到磁盘,如果已经加载缓存,则下次加载,否则展示返回内容 |
http | 容灾字段,如果http表示终端六个小时之内不会采用sonic请求该URL |
模式介绍
VasSonic根据本地是否有缓存以及本地缓存数据跟服务器数据的差异情况分为以下四种模式。
模式 | 说明 | 条件 |
---|---|---|
首次加载 | 本地没有缓存,即第一次加载页面 | etag为空值或template_tag为空值 |
完全缓存 | 本地有缓存,且缓存内容跟服务器内容完全一样 | etag一致 |
数据更新 | 本地有缓存,本地模版内容跟服务器模版内容一样,但数据块有变化 | etag不一致 且 template_tag一致 |
模版更新 | 本地有缓存,缓存的模版内容跟服务器的模版内容不一样 | etag不一致 且 template_tag不一致 |
动态缓存
客户端第一次加载符合VasSonic规范的页面后,延迟几秒后(避免激烈IO竞争),会将页面抽离成模板和数据并保存到本地。此时终端缓存目录下,该页面将对应三个缓存文件xxx.html、xxx.template、xxx.data,其中xxx是该页面的唯一标识(即sonicSessionId)。
对于页面非首次加载场景,VasSonic优先加载本地缓存,终端请求并计算差异数据块,最终通知页面刷新变化元素即可完成增量更新。(注意:根据请求字段不同会出现前面讨论的四种模式,其他模式逻辑比较简单,这里仅讨论数据更新模式)主要涉及两个问题:如何获取差异数据以及如何局部刷新。
增量更新之获取数据
数据更新模式下,后台会在响应头部返回template-change=false等字段,同时响应包体返回的内容不再是完整的html,而是全部的数据块。此时客户端通过将本地的数据块和服务器返回的数据库进行diff对比,可以得到变化的数据块(diff_data)。
增量更新之局部刷新
获得变化数据块(diff_data)后,客户端只需要通知页面页面设置的回调接口(getDiffDataCallback)进行界面元素更新即可。这里javascript的通信方式也可以自由定义(可以使用webview标准的javascript通信方式,也可以使用伪协议的方式),只要页面跟终端协商一致就可以。
页面跟终端定义好通信方式之后就可以通过请求获取最新的数据。页面收到终端的返回数据之后根据返回数据中的code字段判断当前sonic的模式。
首次加载如何秒开
VasSonic缓存更新等逻辑可以脱离WebView执行,因此VasSonic可以随时随地进行预加载,当用户真正点击页面的时候,做到无缝连接实现秒开!
目前主要使用到以下两种场景中:
1 重要的活动页面预埋,可以通过后台push的方式提前预加载;2 预测用户的打开页面行为,提前对预测页面进行预加载;
页面唯一标识:sessionId
在VasSonic中,我们通过sessionId来唯一标识一个URL。默认的拼接规则为:
sessionId = [user_account_id + "_"] + MD5(URL.authority + URL.path + URL.sonic_remain_params)
字段 | 说明 |
---|---|
useraccount_id | 当前用户的账号,可选值,由当SonicSessionConfig.isAccountRelated决定 |
MD5 | MD5函数 |
URL.authority | 当前URL域名的认证机构,一般为null |
URL.path | 当前URL的全路径,不包含参数部分 |
URL.sonic_remain_params | 当前URL的sonic保留字段,sonic前缀或者sonic_remain_params=xxx |
举两个栗子:
Example 1:
user_account_id = "kid" url = "https://bob@gxh.vip.qq.com/club/themes/mobile/bq/html/index.html?id=2&type=1&sonic_remain_params=id" sessionId = "[kid_]" + MD5("bob" + "gxh.vip.qq.com/club/themes/mobile/bq/html/index.html" + "id=2")
Example 2:
user_account_id = "kid" url_1 = https://bob@gxh.vip.qq.com/club/themes/mobile/bq/html/index.html?id=2&type=1 url_2 = https://bob@gxh.vip.qq.com/club/themes/mobile/bq/html/index.html?id=2&type=2&sonic_remain_params=id url_3 = https://bob@gxh.vip.qq.com/club/themes/mobile/bq/html/index.html?id=2&type=1&btest=1 sessionId(url_1) = sessionId(url_3) != sessionId(url_2)
注意:在VasSonic框架中,相同sessionId对应的SonicSession仅能同时存在一个,主要出于两个原因考虑:
- 如果同时运行多个相关的SonicSession的话,会导致缓存管理复杂化,且容易出现相互覆盖问题;
- 在移动应用的正常业务场景中,极少出现同时打开同一个URL,万一出现这个场景,将URL回退为标准WebView流程执行即可。