【前端性能優化指南】1 - 利用緩存減小遠程請求

更多前端性能優化系列請點擊這裏 >>前端

歡迎來到「前端性能優化之旅」的第一站 —— 緩存。git

當瀏覽器想要獲取遠程的數據時,咱們的性能之旅就開始了。然而,咱們並不會當即動身(發送請求)。在計算機領域,不少性能問題都會經過增長緩存來解決,前端也不例外。和許多後端服務同樣,前端緩存也是多級的。下面讓咱們一塊兒來具體看一看。github

1. 本地數據存儲

經過結合本地存儲,能夠在業務代碼側實現緩存。web

對於一些請求,咱們能夠直接在業務代碼側進行緩存處理。緩存方式包括 localStoragesessionStorageindexedDB。把這塊加入緩存的討論也許會有爭議,但利用好它確實能在程序側達到一些相似緩存的能力。後端

例如,咱們的頁面上有一個日更新的榜單,咱們能夠作一個當日緩存:瀏覽器

// 當用戶加載站點中的榜單組件時,能夠經過該方法獲取榜單數據
async function readListData() {
    const info = JSON.parse(localStorage.getItem('listInfo'));
    if (isExpired(info.time, +(new Date))) {
        const list = await fetchList();
        localStorage.setItem('listInfo', JSON.stringify({
            time: +(new Date),
            list: list
        }));
        return list;
    }
    return info.list;
}
複製代碼

localStorage 你們都比較瞭解了,indexedDB 可能會了解的更少一些。想快速瞭解 indexedDB 使用方式能夠看這篇文章[1]緩存

從前端視角看,這是一種本地存儲;但若是從整個系統的維度來看,不少時候其實也是緩存鏈條中的一環。對於一些特殊的、輕量級的業務數據,能夠考慮使用本地存儲做爲緩存。性能優化

2. 內存緩存(Memory)

當你訪問一個頁面及其子資源時,有時候會出現一個資源被使用屢次,例如圖標。因爲該資源已經存儲在內存中,再去請求反而畫蛇添足,瀏覽器內存則是最近、最快的響應場所。服務器

內存緩存並沒有明確的標準規定,它與 HTTP 語義下的緩存關聯性不大,算是瀏覽器幫咱們實現的優化,不少時候其實咱們意識不到。網絡

對內存緩存感興趣,能夠在這篇文章[2]的 Memory Cache 部分進一步瞭解。

3. Cache API

當咱們沒有命中內存緩存時,是否就開始發送請求了呢?其實不必定。

在這時咱們還可能會碰到 Cache API 裏的緩存,提到它就不得不提一下 Service Worker 了。它們一般都是配合使用的。

首先明確一下,這層的緩存沒有規定說該緩存什麼、什麼狀況下須要緩存,它只是提供給了客戶端構建請求緩存機制的能力。若是你對 PWA 或者 Service Worker 很瞭解,應該很是清楚是怎麼一回事。若是不瞭解也沒有關係,咱們能夠簡單看一下:

首先,Service Worker 是一個後臺運行的獨立線程,能夠在代碼中啓用

// index.js
if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('./sw.js').then(function () {
        // 註冊成功
    });
}
複製代碼

以後須要處理一些 Service Worker 的生命週期事件,而其中與這裏提到的緩存功能直接相關的則是請求攔截:

// sw.js
self.addEventListener('fetch', function (e) {
    // 若是有cache則直接返回,不然經過fetch請求
    e.respondWith(
        caches.match(e.request).then(function (cache) {
            return cache || fetch(e.request);
        }).catch(function (err) {
            console.log(err);
            return fetch(e.request);
        })
    );
});
複製代碼

以上代碼會攔截全部的網絡請求,查看是否有緩存的請求內容,若是有則返回緩存,不然會繼續發送請求。與內存緩存不一樣,Cache API 提供的緩存能夠認爲是「永久性」的,關閉瀏覽器或離開頁面以後,下次再訪問仍然可使用。

Service Worker 與 Cache API 實際上是一個功能很是強大的組合,可以實現堆業務的透明,在兼容性上也能夠作成漸進支持。仍是很是推薦在業務中嘗試的。固然上面代碼簡略了不少,想要進一步瞭解 Service Worker 和 Cache API 的使用能夠看這篇文章[3]。同時推薦使用 Google 的 Workbox

4. HTTP 緩存

若是 Service Worker 中也沒有緩存的請求信息,那麼就會真正到 HTTP request 的階段了。這個時候出現的就是咱們所熟知的 HTTP 緩存規範。

HTTP 有一系列的規範來規定哪些狀況下須要緩存請求信息、緩存多久,而哪些狀況下不能進行信息的緩存。咱們能夠經過相關的 HTTP 請求頭來實現緩存。

HTTP 緩存大體能夠分爲強緩存與協商緩存。

4.1. 強緩存

在強緩存的狀況下,瀏覽器不會向服務器發送請求,而是直接從本地緩存中讀取內容,這個「本地」通常就是來源於硬盤。這也就是咱們在 Chrome DevTools 上常常看到的「disk cache」。

與其相關的響應頭則是 ExpiresCache-Control。在 Expires 上能夠設置一個過時時間,瀏覽器經過將其與當前本地時間對比,判斷資源是否過時,未過時則直接從本地取便可。而 Cache-Control 則能夠經過給它設置一個 max-age,來控制過時時間。例如,max-age=300 就是表示在響應成功後 300 秒內,資源請求會走強緩存。

4.2. 協商緩存

你可能也感受到了,強緩存不是那麼靈活。若是我在 300 秒內更新了資源,須要怎麼通知客戶端呢?經常使用的方式就是經過協商緩存。

咱們知道,遠程請求慢的一大緣由就是報文體積較大。協商緩存就是但願能經過先「問一問」服務器資源到底有沒有過時,來避免無謂的資源下載。這伴隨的每每會是 HTTP 請求中的 304 響應碼。下面簡單介紹一下實現協商緩存的兩種方式:

一種協防緩存的方式是:服務器第一次響應時返回 Last-Modified,而瀏覽器在後續請求時帶上其值做爲 If-Modified-Since,至關於問服務端:XX 時間點以後,這個資源更新了麼?服務器根據實際狀況回答便可:更新了(狀態碼 200)或沒更新(狀態碼 304)。

上面是經過時間來判斷是否更新,若是更新時間間隔太短,例如 1s 一下,那麼使用更新時間的方式精度就不夠了。因此還有一種是經過標識 —— ETag。服務器第一次響應時返回 ETag,而瀏覽器在後續請求時帶上其值做爲 If-None-Match。通常會用文件的 MD5 做爲 ETag

做爲前端工程師,必定要善於應用 HTTP 緩存。若是想要了解更多關於 HTTP 緩存的內容,能夠閱讀這篇文章[4]

上面這些的各級緩存的匹配機制裏,都是包含資源的 uri 的匹配,即 uri 更改後不會命中緩存。也正是如此,咱們目前在前端實踐中都會把文件 HASH 加入到文件名中,避免同名文件命中緩存的舊資源。

5. Push Cache

假如很不幸,以上這些緩存你都沒有命中,那麼你將會碰到最後一個緩存檢查 —— Push Cache。

Push Cache 實際上是 HTTP/2 的 Push 功能所帶來的。簡言之,過去一個 HTTP 的請求鏈接只能傳輸一個資源,而如今你在請求一個資源的同時,服務端能夠爲你「推送」一些其餘資源 —— 你可能在在不久的未來就會用到一些資源。例如,你在請求 www.sample.com 時,服務端不只發送了頁面文檔,還一塊兒推送了 關鍵 CSS 樣式表。這也就避免了瀏覽器收到響應、解析到相應位置時纔會請求所帶來的延後。

不過 HTTP/2 Push Cache 是一個比較底層的網絡特性,與其餘的緩存有不少不一樣,例如:

  • 當匹配上時,並不會在額外檢查資源是否過時;
  • 存活時間很短,甚至短過內存緩存(例若有文章提到,Chrome 中爲 5min 左右);
  • 只會被使用一次;
  • HTTP/2 鏈接斷開將致使緩存直接失效;
  • ……

若是對 HTTP/2 Push 感興趣,能夠看看這篇文章[5]


好了,到目前爲止,咱們可能尚未發出一個真正的請求。這也意味着,在緩存檢查階段咱們就會有不少機會將後續的性能問題扼殺在搖籃之中 —— 若是遠程請求都沒必要發出,又何必優化加載性能呢?

因此,審視一下咱們的應用、業務,看看哪些性能問題是能夠在源頭上解決的。

不過不少時候,能經過緩存解決的問題只有一部分。因此下面咱們會繼續這趟旅行,目前咱們已經有了一個好的開始,不是麼?


目前內容已所有更新至 ✨ fe-performance-journey ✨ 倉庫中,陸續會將內容同步到掘金上。若是但願儘快閱讀相關內容,能夠直接去該倉庫中瀏覽文章。

喜歡的朋友能夠 star 一下,後續也會繼續更新更多性能優化相關的內容。


參考資料

  1. A quick but complete guide to IndexedDB and storing data in browsers
  2. A Tale of Four Caches
  3. PWA學習與實踐:讓你的WebApp離線可用
  4. 瀏覽器緩存機制:強緩存、協商緩存
  5. HTTP/2 push is tougher than I thought
  6. Caching best practices & max-age gotchas
  7. The Offline Cookbook (Service Worker)
  8. HTTP/2 ORG
  9. Web Caching Explained by Buying Milk at the Supermarket
  10. 深刻理解瀏覽器的緩存機制
相關文章
相關標籤/搜索