溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何實(shí)現(xiàn)web前端資源增量式更新

發(fā)布時(shí)間:2021-11-15 15:19:42 來源:億速云 閱讀:148 作者:iii 欄目:web開發(fā)

這篇文章主要講解了“如何實(shí)現(xiàn)web前端資源增量式更新”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“如何實(shí)現(xiàn)web前端資源增量式更新”吧!

之前校招面試的時(shí)候遇到過一個(gè)很有趣的問題:

“假設(shè)有一個(gè)超超超大型的Web項(xiàng)目,JS源代碼壓縮之后超過10MB(實(shí)際怎么可能會(huì)有這么大的嘛=。=),每次更新之后都需要用戶重新加載JS是不可接受的,那么怎么樣從工程的角度解決這種問題?”

一開始立馬想到了好幾點(diǎn)解決方案,比如:

  1. 抽出基礎(chǔ)的不常更新的模塊作為長(zhǎng)期緩存;

  2. 如果使用了 React 或者 Vue2.0 這樣支持服務(wù)器端渲染的框架的話,就采用服務(wù)器端渲染然后再逐步分塊加載 JS 的方法;

  3. 如果是 Hybrid 開發(fā)的話,可以考慮使用本地資源加載,類似“離線包”的想法(之前在騰訊實(shí)習(xí)的時(shí)候天天遇到這東西)。

后來在面試官的引導(dǎo)下想到了一種“增量式更新”的解決方案,簡(jiǎn)單地說就是在版本更新的時(shí)候不需要重新加載資源,只需要加載一段很小的 diff  信息,然后合并到當(dāng)前資源上,類似 git merge 的效果。

1、用戶端使用 LocalStorage 或者其它儲(chǔ)存方案,存儲(chǔ)一份原始代碼+時(shí)間戳:

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

2、每次加載資源的時(shí)候向服務(wù)器發(fā)送這個(gè)時(shí)間戳;

3、服務(wù)器從接受到時(shí)間戳中識(shí)別出客戶端的版本,和***的版本做一次 diff,返回兩者的 diff 信息:

diff("aaabbbccc", "aaagggccc");  // 假設(shè)我們的diff信息這樣表示:  // [3, "-3", "+ggg", 3]

4、客戶端接收到這個(gè) diff 信息之后,把本地資源和時(shí)間戳更新到***,實(shí)現(xiàn)一次增量更新:

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

實(shí)踐

下面把這個(gè)方案中的核心思想實(shí)現(xiàn)一遍,簡(jiǎn)單地說就是實(shí)現(xiàn) diff 和 mergeDiff 兩個(gè)函數(shù)。

今天找到了一個(gè)不錯(cuò)的 diff 算法:

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

我們只需要調(diào)用它的 diffChars 方法來對(duì)比兩個(gè)字符串的差異:

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 信息略有些冗余,我們可以自定義一種更簡(jiǎn)潔的表示方法來加速傳輸?shù)乃俣龋?/p>

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

整數(shù)代表無變化的字符數(shù)量,“-”開頭的字符串代表被移除的字符數(shù)量,“+”開頭的字符串代表新加入的字符。所以我們可以寫一個(gè) minimizeDiffInfo  函數(shù):

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]'

用戶端接受到精簡(jiǎn)之后的 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;  }

實(shí)際效果

有興趣的話可以直接運(yùn)行這個(gè):

GitHub &ndash; starkwang/Incremental

使用 create-react-app 這個(gè)小工具快速生成了一個(gè) React 項(xiàng)目,隨便改了兩行代碼,然后對(duì)比了一下build之后的新舊兩個(gè)版本:

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);

下面是結(jié)果:

如何實(shí)現(xiàn)web前端資源增量式更新

可以看到 build 之后的代碼有 21w 多個(gè)字符(212KB),而 diff  信息卻相當(dāng)短小,只有151個(gè)字符,相比于重新加載新版本,縮小了1000多倍(當(dāng)然我這里只改了兩三行代碼,小是自然的)。

一些沒涉及到的問題

上面只是把核心的思路實(shí)現(xiàn)了一遍,實(shí)際工程中還有更多要考慮的東西:

1、服務(wù)器不可能對(duì)每一次請(qǐng)求都重新計(jì)算一次 diff,所以必然要對(duì) diff 信息做緩存;

2、用戶端持久化儲(chǔ)存的實(shí)現(xiàn)方案,比如喜聞樂見的 LocalStorage、Indexed DB、Web SQL,或者使用 native app  提供的接口;

3、容錯(cuò)、用戶端和服務(wù)器端的一致性校對(duì)、強(qiáng)制刷新的實(shí)現(xiàn)。

感謝各位的閱讀,以上就是“如何實(shí)現(xiàn)web前端資源增量式更新”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對(duì)如何實(shí)現(xiàn)web前端資源增量式更新這一問題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

web
AI