前言
對於前端工程化而言,靜態資源的快取與更新一直是一個比較大的問題,各大公司也推出了各自的解決方案,如百度的FIS工具集。如果沒有解決好這個問題,不僅會給使用者造成糟糕的使用者體驗,而且還會給開發和除錯帶了很多不必要的麻煩。關於如何自動實現快取更新,以下是自己的一點心得和體會。
靜態資源釋出的痛點
我們知道,快取對於前端效能的優化是十分重要的,在正式釋出系統的時候,對於那些不經常變動的靜態資源比如各種JS工具庫、CSS檔案、背景圖片等等我們會設定一個比較大的快取過期時間(max-age),當使用者再次訪問這個頁面的時候就可以直接利用快取而不是重新從伺服器獲取,這樣不僅可以減輕服務端的壓力,還可以節約網路傳輸的流量,同時使用者體驗也更好(使用者開啟頁面更快了)。這樣看起來很完美,你好我好大家都好,but,理想是美好的,現實是殘酷的,假設存在這樣一個瀏覽器,強制快取靜態資源還不給你清除快取的機會(微信,說的就是你!),該怎麼辦?即使你的服務端已更新,檔案的Etag
值已變化,但是微信就是不給你更新檔案…請允許我做一個悲傷的表情…
對於這個問題,我們很自然的想法是在每次釋出新版本的時候給所有靜態資源的請求後面加上一個版本引數或時間戳,類似於/js/indx.js?ver=1.0.1
,但是這樣存在兩個問題:
- 微信對於加引數的靜態資源還是優先使用快取版本(實際測試的情況是這樣的)。
- 假如這樣是可行的,那麼對於沒有變更的靜態資源也會重新從伺服器獲取而不是讀取快取,沒有充分利用快取。
那麼有沒有一種方法可以自動分辨出哪個檔案發生了變化並讓客戶端主動更新呢?答案是肯定的。我們知道一個檔案的MD5
可以唯一標識一個檔案。若檔案發生了變化,檔案的指紋值MD5
也隨之變化。利用這個特性我們就可以標識出哪個靜態資源發生了變化,並讓客戶端主動更新。
如何解決?
經過前文的介紹,我們知道了可以利用檔案的指紋值來標識需要客戶端主動更新的檔案,但是如何實現呢?經過自己的思考和調研後,大致思路為:
- 在每次釋出之前,利用
Gulp
對所有的靜態資源進行預處理,重新命名為原檔名
+檔案MD5值
+檔案字尾名
的形式。比如index.js
重新命名為index-c6c9492ce6.js
。 - 生成一份
manifest
,標明瞭預處理前後檔案之間的對應關係.manifest
檔案的樣子為:
1234567{"index.js": "index-c6c9492ce6.js","lib/jQuery/jQuery.js": "lib/jQuery/jQuery-683c73084c.js","require.js": "require-c8e8015f8d.js","style.css": "style-125d3a3f82.css","tools.js": "tools-5666ee48e9.js"} - 在渲染檢視模版的時候,根據
manifest
,將預處理前的靜態資置換為預處理後的靜態資源。 - 如果在瀏覽器端用到了模組載入器(這裡以實現了AMD標準的
requireJS
為例),在每次釋出的時候需要根據manifest
對模組進行mapping,將配置檔案以內聯JS的形式寫入到模版頁面裡面,類似於:
12345678910111213<script>requirejs.config({"baseUrl": "/js","map": {"*": {"index": "index-c6c9492ce6","jquery": "lib/jQuery/jQuery-683c73084c","require": "require-c8e8015f8d","tools": "tools-5666ee48e9"}}});</script>
測試
為了驗證可行性,自己做了個demo,程式碼託管在Github。經測試,可以完美的解決之前提出的問題。
我們發現,只有index.js
在更改後被主動更新了,其餘的靜態資源均是直接利用的快取!。
後記
關於前端效能優化,快取一直是濃墨重彩的一筆。如果利用好快取控制,不僅能提高使用者體驗,減少服務端流量壓力,而且對於前端工程化的推進也是很有幫助的。隨著web系統的業務和功能的擴大,維護前端的任務將變得越來越繁重,按照歷史規律,當一件事變得越來越繁重的時候,工程化是其唯一的出路。現在的前端還很年輕,工程化的概念提出來不久,但我相信,在各大網際網路公司的前端們積極推動下,前端工程化必將成為業界標配。
打賞支援我寫出更多好文章,謝謝!
打賞作者
打賞支援我寫出更多好文章,謝謝!
任選一種支付方式