从14年开始,手机百度就开始支持localstorage的细粒度缓存,配合inline渲染模式使用,在2G慢速网站将页面的js和css嵌入 到script和style标签,然后将源码存到localstorage,第二次访问的时候从localstorage读取,提高页面访问速度。
细粒度localstorage方案
最大的缺点是:需要页面渲染之后,读取localstorage缓存内容,然后二次拉取没有缓存或需要更新的资源。
还有一个方案,就是利用cookie保存一个版本号信息,server端拿到cookie就可以判断出来需要下发和更新的资源。这个方案虽然可以避 免二次拉取,但是毕竟cookie的存储量有限,cookie太大就影响http上行请求速度。所以之前百度移动搜索结果页的方法就是整个页面存储一个 md5的版本号,如果有一个资源更新,那么就需要整个页面的资源代码重新下发,所以往往每周上线的时候会引起页面性能数据抖动。
第一步:代码按照更新频度进行分组
上面提到的cookie方案最大的问题就是:业务代码和通用代码都“一视同仁”,这样经常频繁变化的业务代码只要发生变化,通用代码也需要更新,所以每周固定上线时间就会全部重新下发一遍代码。
为了解决这个方案的缺点,首先将需要缓存的代码进行分层:通用 和 业务。通过区分通用和业务代码,每段代码都有自己的独立版本号,业务层代码的修改就不会引起通用代码的更新,只会更新自己的版本号,下发自己的新版本。
第二步:利用cookie的多维度
大家都知道cookie是可以按照域名来缓存的,二级子域名可以读取主域名的cookie,这个我们称之为:cookie的doman维度。
cookie除了域名之外,还有一个容易被忽略的维度,就是不同的pathcookie也可以不同,下一级path是可以读取上一级path的cookie。这个是cookie的第二个维度:cookie的path维度
下面例子是同一个cookiepo_lsv在不同domain和path的情况。
key | value | domain | path |
---|---|---|---|
po_lsv | jZ-1_jA-1_cF-1_cM-1_jB-2 | po.m.baidu.com | /tiny |
po_lsv | jZ-1_jA-1_cF-1_cM-1_jB-1 | po.m.baidu.com | / |
po_lsv | jZ-1_jA-1_cF-1 | m.baidu.com | / |
保证cookie保存足够多的信息
之前使用md5,cookie value的长度是32个长度,如果利用value足够短的长度保存足够多的cookie信息。我们的方法是使用如下约定:
- css和js分别使用c和j来打头
- localstorage的key和版本号使用-间隔,而每个版本之间使用_,保证cookie在encode的时候不被编码成%xx保证长度
- 真实localstorage的值,使用尽量一个字母简写
- 而版本号使用的是36进制,如果每周上线一次计算,加上cookie有效期,36进制保证版本号是一位已经足够了,不用考虑版本号重叠问题
自动打包工具
维护版本号是很麻烦的一个工作,如果一不留神漏了哪个,就会导致重大bug,比如页面一直reload等,所以我们使用了打包工具来完成这个过程,避免了人工的错误。
localstorage.json的格式内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
{ "jA" : { "hash" : "2c79d70" , "files" : [ "common:widget/localstorage/zepto-ajax.tpl" ], "version" : 1 }, "jZ" : { "hash" : "5358395" , "files" : [ "common:widget/localstorage/zepto.tpl" ], "version" : 1 } } |
需要缓存到localstorage的文件都统一放在localstorage文件夹,再看下需要缓存的widget经过打包工具自动生成的代码:
1
2
3
|
<script data-lsid= "jZ" > __inline( '/static/js/zepto.js' ); </script> |
经过打包工具release之后,变成下面的smarty模板:
1
2
3
4
5
6
7
|
{% if ($_ls_nonsupport) || ($_parsedLSCookies.jZ.isUpdate )%} <script data-lsv= "{%$_parsedLSCookies.jZ.version|escape:html%}" data-lsid= "jZ" > var Zepto=xxx </script> {% else %} <script>LS.exec( "jZ" , "js" );</script> {%/ if %} |
代码执行时候,解析cookie,然后读取localstroage.json的内容,跟cookie的版本号对比,生成模板变量,赋值给模板文件。