【转】基于localStorage的资源离线和更新技术
ServiceWorker的资源离线与更新
ServiceWorker是替代Application Cache的机制,目前为止其兼容性很差。
localStorage资源离线缓存与更新
基本思路:将JavaScript、CSS资源文件甚至是接口返回的数据资源缓存到浏览器的localStorage中,下次打开页面时不进行JavaScript、CSS资源的请求,而是直接通过localStorage读取内容,然后插入到页面中解析执行。
HTML
<div id="versionStore" data-version="2"></div>
JS
/** * localStorage方式实现增量更新 */ let localStorage = window.localStorage, oldVersion = localStorage.getItem('version') || 0, // version记录localStorage中存的版本 newVersion = +document.querySelector('#versionStore').getAttribute('data-version'); // 当前最新版本 let content = null; if(newVersion > oldVersion) { // 内容有更新或第一次加载 const xhr = new XMLHttpRequest(); xhr.onreadystatechange = function(event){ if (xhr.readyState == 4){ if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){ content = xhr.responseText; updateScript(content); // 更新脚本 localStorage.setItem('version', newVersion); // 更新本地版本 localStorage.setItem('a.js', content); // 更新本地内容 } } }; xhr.open("get", "index.js", true); xhr.send(null); } else { // 无内容更新 content = localStorage.getItem('a.js'); // 直接获取本地内容 updateScript(content) } /** * 更新页面脚本 * @param content 脚本内容 */ function updateScript(content) { let script = document.createElement('script'); script.innerHTML = content; document.body.appendChild(script); script = null; }
页面脚本通过获取页面上的“最新版本号”和本地localStorage保存的“旧版本号”进行对比。如果本地没有版本号或者版本号较旧,则加载最新版本的静态资源文件到页面上,同时更新本地原有的localStorage缓存的内容和版本号;否则直接读取localStorage的静态资源内容到页面中解析执行。
缺点:(1)localStorage的大小限制,同域下一般是5MB;(2)用户手动清空localStorage会使离线资源失效;(3)读取localStorage的速度比较慢,尤其是移动端。
基于增量文件的更新方式
如果一个文件中只修改了少量字符,上述方式会导致整个资源文件的更新。为了节省流量我们需要增量更新。假设我们已有1.1,1.2,1.3三个版本发布,现在需要进行1.4的发布上线。为了满足增量更新,我们需要根据前面的三个版本文件内容与最新版本内容进行对比分析,分别生成三个不同版本的增量文件1.1-1.4.js,1.2-1.4.js,1.3-1.4.js,同时保留1.4版本的全量文件。
// 实现方式跟上述类似,只需修改请求地址 xhr.open("get", `${oldVersion}-${newVersion}.js`, true);
基于文件代码分块的增量更新机制
根据大小确定分割字符数(比如10个字符),文件字符串由几个字符串块连接组成chunk1-chunk2-chunk3-chunk4,此时需要在chunk1和chunk2之间添加data1,chunk3内容修改为chunk5,chunk4的块被删除。
[1, data1, 2, chunk5, -4]
1表示原来chunk1的内容不变,data1表示插入的新内容;-4表示删除chunk4的文件块;
let a = 1, b = 1; console.log(a, b); // 修改为 let a = 2, b = 2; console.log(a + b);
基于编辑距离的增量更新机制
上面方式节约资源的量取决于块的大小和内容变化的块序号分布。根据编辑距离算法增量更新的方式可以真正做到字符级别的更新。它指的是从一个字符串变换到另一个字符串所需要的最少变化操作步骤。如果能计算获取两个文件对比变化时每个字符的操作步骤,就可以将操作步骤作为增量文件下载,然后在浏览器端进行代码的运算更新了。不过这种情况对于少量的字符更新很有用,如果一次更新的内容很多,生成的增量文件很可能比源文件还大,所以实际使用过程中需要结合具体情况,在上述两种增量方式中选择。
资源覆盖率统计
有了前端资源的离线和更新机制,就要考虑在每次新资源包发布后统计新版本的更新覆盖率。这对于增量更新尤为重要,如果发现某个版本的使用为0或接近0,该版本就无需在维护。方式有很多中,最简单的就是上报版本号,每次PV统计时带上版本号,最后根据PV中的版本号来统计访问不同版本上用户的分布情况。