实现前端资源增量式更新的一种思路

数据库2025-11-05 09:42:1658553

之前校招面试的实现式更时候遇到过一个很有趣的问题:

“假设有一个超超超大型的Web项目,JS源代码压缩之后超过10MB(实际怎么可能会有这么大的前端嘛=。=),资源增量种思每次更新之后都需要用户重新加载JS是实现式更不可接受的,那么怎么样从工程的前端角度解决这种问题?”

一开始立马想到了好几点解决方案,比如:

抽出基础的资源增量种思不常更新的模块作为长期缓存; 如果使用了 React 或者 Vue2.0 这样支持服务器端渲染的框架的话,就采用服务器端渲染然后再逐步分块加载 JS 的实现式更方法; 如果是 Hybrid 开发的话,可以考虑使用本地资源加载,前端类似“离线包”的资源增量种思想法(之前在腾讯实习的时候天天遇到这东西)。

后来在面试官的实现式更引导下想到了一种“增量式更新”的解决方案,简单地说就是前端在版本更新的时候不需要重新加载资源,只需要加载一段很小的资源增量种思 diff 信息,然后合并到当前资源上,IT技术网实现式更类似 git merge 的前端效果。

1、资源增量种思用户端使用 LocalStorage 或者其它储存方案,存储一份原始代码+时间戳:

{     timeStamp: "20161026xxxxxx",     data: "aaabbbccc" }  

2、每次加载资源的时候向服务器发送这个时间戳;

3、服务器从接受到时间戳中识别出客户端的版本,和***的版本做一次 diff,返回两者的 diff 信息:

diff("aaabbbccc", "aaagggccc"); // 假设我们的diff信息这样表示: // [3, "-3", "+ggg", 3]  

4、客户端接收到这个 diff 信息之后,把本地资源和时间戳更新到***,实现一次增量更新:

mergeDiff("aaabbbccc", [3, "-3", "+ggg", 3]); //=> "aaagggccc"  

实践

下面把这个方案中的核心思想实现一遍,简单地说就是实现 diff 和 mergeDiff 两个函数。

今天找到了一个不错的 diff 算法:

GitHub – kpdecker/jsdiff: A javascript text differencing implementation.

我们只需要调用它的 diffChars 方法来对比两个字符串的差异:

var oldStr = aaabbbccc; var newStr = aaagggccc; JsDiff.diffChars(oldStr, newStr); //=> //[ { count: 3, value: aaa }, //  { count: 3, added: undefined, removed: true, value: bbb }, //  { count: 3, added: true, removed: undefined, value: ggg }, //  { count: 3, value: ccc } ]  

上面的 diff 信息略有些冗余,我们可以自定义一种更简洁的表示方法来加速传输的速度:

[3, "-3", "+ggg", 3] 

整数代表无变化的字符数量,“-”开头的字符串代表被移除的字符数量,“+”开头的企商汇字符串代表新加入的字符。所以我们可以写一个 minimizeDiffInfo 函数:

function minimizeDiffInfo(originalInfo){     var result = originalInfo.map(info => {         if(info.added){             return + + info.value;         }         if(info.removed){             return - + info.count;         }         return info.count;     });     return JSON.stringify(result); } var diffInfo = [     { count: 3, value: aaa },     { count: 3, added: undefined, removed: true, value: bbb },     { count: 3, added: true, removed: undefined, value: ggg },     { count: 3, value: ccc } ]; minimizeDiffInfo(diffInfo); //=> [3, "-3", "+ggg", 3]  

用户端接受到精简之后的 diff 信息,生成***的资源:

mergeDiff(aaabbbccc, [3, "-3", "+ggg", 3]); //=> aaagggccc function mergeDiff(oldString, diffInfo){     var newString = ;     var diffInfo = JSON.parse(diffInfo);     var p = 0;     for(var i = 0; i < diffInfo.length; i++){         var info = diffInfo[i];         if(typeof(info) == number){             newString += oldString.slice(p, p + info);             p += info;             continue;         }         if(typeof(info) == string){             if(info[0] === +){                 var addedString = info.slice(1, info.length);                 newString += addedString;             }             if(info[0] === -){                 var removedCount = parseInt(info.slice(1, info.length));                 p += removedCount;             }         }     }     return newString; }  

实际效果

有兴趣的话可以直接运行这个:

GitHub – starkwang/Incremental

使用 create-react-app 这个小工具快速生成了一个 React 项目,随便改了两行代码,然后对比了一下build之后的新旧两个版本:

var JsDiff = require(diff); var fs = require(fs); var newFile = fs.readFileSync(a.js, utf-8); var oldFile = fs.readFileSync(b.js, utf-8); console.log(New File Length: , newFile.length); console.log(Old File Length: , oldFile.length); var diffInfo  = getDiffInfo(JsDiff.diffChars(oldFile, newFile)); console.log(diffInfo Length: , diffInfo.length); console.log(diffInfo); var result = mergeDiff(oldFile, diffInfo); console.log(result === newFile);  

下面是结果:

 

可以看到 build 之后的代码有 21w 多个字符(212KB),而 diff 信息却相当短小,只有151个字符,相比于重新加载新版本,缩小了1000多倍(当然我这里只改了两三行代码,小是自然的)。

一些没涉及到的问题

上面只是把核心的思路实现了一遍,实际工程中还有更多要考虑的东西:

1、服务器不可能对每一次请求都重新计算一次 diff,所以必然要对 diff 信息做缓存;

2、用户端持久化储存的实现方案,比如喜闻乐见的 LocalStorage、Indexed DB、Web SQL,云南idc服务商或者使用 native app 提供的接口;

3、容错、用户端和服务器端的一致性校对、强制刷新的实现。 

本文地址:http://www.bzve.cn/html/367f66098972.html
版权声明

本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。

热门文章

全站热门

windows之外的世界比想像中要大得多呢,几乎天天都在用ssh,却到今天才知道有sshfs这个好东西,前几天还在为ZendStudio可以远程编辑文件欣喜,惭愧啊,终于有比vi scp://hostname//path/to/file更方便的方式了。sshfs,就是可以把ssh连接到的主机资源,映射到本机的文件系统当中,然后用户可以像操作本地文件一样操作,而实际的文件改动将通过ssh传输到远程主机当中。类似于mount了一个windows共享目录,或者另外一台linux主机的samba共享,或者是nfs共享,只是“共享协议”变成了ssh通道罢了。ubuntu源中已经包含了sshfs了,以及所需的fuse包,直接apt安装就可以了,一共是三个包:“fuse-utils libfuse2 sshfs”,不大,加起来才179k。安装的时候会自动创建用户组fuse,要使用sshfs的用户需要先加到这个用户组里才行(记得完成后还要重新登录),不然会没有权限执行fusermount:fuse: failed to exec fusermount: Permission denied复制代码代码如下:复制代码代码如下:复制代码代码如下:sudo umount ~/remote_folder这个简单的命令会断开远程连接同时清空 remote_folder 目录。总结在 Linux 上有很多工具可以用于访问远程文件并挂载到本地。但是如之前所说,假如有的话,也只有很少的工具能充分利用 SSH 的强大功能。我希望在这篇指南的帮助下,也能认识到 SSHfs 是一个多么强大的工具。

i33220能否满足游戏需求?(性能分析与游戏推荐)

800米高空4G信号的覆盖状况及影响(高空信号传输技术的关键性分析与未来展望)

利用PE安装引导实现系统修复与重装

苹果电脑开机重装系统教程(简单易懂的操作指南,让您的苹果电脑焕然一新)

制作电脑U盘启动盘的简明教程(一步步教你制作可靠的电脑U盘启动盘)

华帝百得热水器的优势与用户体验(华帝百得热水器为您带来舒适洗浴体验,让您感受到温暖的力量)

U盘系统刷机教程(详解如何使用U盘刷机及注意事项)

热门文章

友情链接

滇ICP备2023006006号-39