天天看點

【高】HTTP 緩存政策你真的有了解嗎?

作者:成都程式員晴小篆

HTTP緩存政策是指浏覽器和伺服器之間在傳輸資源時,如何使用緩存的方式。HTTP緩存的主要目的是減少網絡傳輸的資料量,提高頁面的通路速度。

緩存的主要政策有哪些?

HTTP緩存政策主要包括以下幾種:

  • 強緩存:通過設定 HTTP 頭部中的 Expires 或 Cache-Control 字段來指定資源在本地緩存的有效期。當資源未過期時,浏覽器直接從緩存中讀取,不會向伺服器發送請求,進而提高頁面的通路速度。
  • 協商緩存:當資源的緩存時間已經過期,浏覽器會向伺服器發送請求,伺服器會檢查資源是否有更新,如果沒有更新,則傳回 304 狀态碼,告訴浏覽器直接使用本地緩存。
    • Last-Modified / If-Modified-Since:伺服器在傳回資源時,會添加 Last-Modified 頭部字段,表示資源最後的修改時間。當浏覽器下次請求該資源時,會在請求頭部添加 If-Modified-Since 字段,表示上次請求時資源的修改時間。伺服器檢查這兩個時間是否一緻,如果一緻,則傳回 304 狀态碼,否則傳回新的資源。
    • ETag / If-None-Match:伺服器在傳回資源時,會添加 ETag 頭部字段,表示資源的唯一辨別。當浏覽器下次請求該資源時,會在請求頭部添加 If-None-Match 字段,表示上次請求時資源的唯一辨別。伺服器檢查這兩個辨別是否一緻,如果一緻,則傳回 304 狀态碼,否則傳回新的資源。
  • 離線緩存:通過使用 HTML5 提供的 Application Cache API,可以将頁面的資源緩存在本地,使得使用者在沒有網絡連接配接的情況下也能夠通路頁面。
  • Service Worker 緩存:Service Worker 是一種在浏覽器背景運作的 JavaScript 線程,可以攔截和處理浏覽器發送的網絡請求。通過使用 Service Worker,可以将頁面的資源緩存在本地,提高頁面的通路速度和使用者體驗。

強緩存中 Expires 或 Cache-Control 有什麼差別?

在 HTTP 緩存政策中,強緩存是指在一定時間内,直接使用本地緩存而不發送請求到伺服器。Expires 和 Cache-Control 是用于設定強緩存的兩種方式。

  • Expires: 是 HTTP/1 的産物,它是一個 HTTP 頭字段,表示資源過期時間,是一個絕對時間。伺服器傳回的 HTTP 頭中,如果包含 Expires 字段,則表示該資源在該過期時間之前可以直接從緩存中擷取,而不需要再次請求伺服器。
  • Cache-Control: 是 HTTP/1.1 的産物,是一個 HTTP 頭字段,用來控制文檔緩存行為。它的值可以是很多不同的指令,例如 max-age、no-cache、no-store、must-revalidate 等等。其中,max-age 指令可以設定資源的最大有效時間,機關是秒。如果伺服器傳回的 HTTP 頭中包含 Cache-Control 指令,則浏覽器會根據該指令的值來決定是否直接使用本地緩存,而不需要再次請求伺服器。

Expires 是一個絕對時間,是以它的缺點是當伺服器的時間與用戶端的時間不一緻時,緩存過期時間就可能會出現偏差。

而 Cache-Control 是一個相對時間,是以它的缺點是需要伺服器和用戶端的時間保持一緻,同時需要正确設定 max-age 的值。

在實際應用中,建議使用 Cache-Control,因為它更加靈活和可控。

離線緩存 Application Cache API 是如何緩存 http 資源的?

Application Cache API(應用程式緩存)是 HTML5 标準中提供的一個用于離線緩存 Web 應用程式的技術。它可以将 Web 應用程式中的檔案(包括 HTML、CSS、JavaScript 和圖像等)儲存到用戶端浏覽器中的緩存中,在沒有網絡連接配接的情況下,仍然能夠通路應用程式。

在 Application Cache API 中,通過在 cache manifest 檔案中列出需要緩存的資源清單來實作離線緩存。該檔案必須以 .appcache 為字尾名,并且必須在 Web 伺服器上進行通路。浏覽器會下載下傳該檔案,并将檔案中列出的資源檔案下載下傳到本地緩存中。當應用程式在離線狀态下打開時,浏覽器會自動從本地緩存中加載緩存的檔案。

下面是一個簡單的 cache manifest 檔案示例:

CACHE MANIFEST
# version 1.0.0

CACHE:
index.html
styles.css
script.js
image.jpg

NETWORK:
*

FALLBACK:
           

上面的示例檔案将緩存 index.html、styles.css、script.js 和 image.jpg 等資源檔案,同時指定 NETWORK 和 FALLBACK,這兩個屬性分别用于指定離線緩存不生效時的網絡連接配接政策和替換資源檔案。

需要注意的是,Application Cache API 并不是一種完美的緩存技術,它也存在一些缺陷。例如,當更新 Web 應用程式時,需要手動清除用戶端浏覽器中的緩存才能生效,否則使用者通路的仍然是舊版本的應用程式。同時,Application Cache API 隻能緩存 GET 請求,不支援 POST 等其他請求方法。是以,為了更好地實作離線緩存,可以使用其他技術,例如 Service Worker。

Service Worker 是如何緩存 http 請求資源的?

ervice Worker 是一種在浏覽器背景運作的腳本,可以攔截和處理浏覽器網絡請求。是以,可以使用 Service Worker 來緩存 http 請求資源。

Service Worker 可以通過以下步驟來緩存 http 請求資源:

  1. 注冊 Service Worker:通過在頁面中注冊 Service Worker,可以告訴浏覽器使用 Service Worker 來處理網絡請求。
  2. 安裝 Service Worker:一旦 Service Worker 被注冊,浏覽器就會下載下傳并安裝它。在安裝過程中,Service Worker 可以緩存一些靜态資源(如 HTML、CSS 和 JavaScript 檔案)。
  3. 激活 Service Worker:一旦 Service Worker 安裝成功,它就可以被激活。在激活過程中,Service Worker 可以删除舊版本的緩存,或者執行其他一些操作。
  4. 攔截網絡請求:一旦 Service Worker 被激活,它就可以攔截浏覽器發送的網絡請求。
  5. 處理網絡請求:當 Service Worker 攔截到網絡請求時,它可以執行一些自定義的邏輯來處理這些請求。例如,它可以檢查緩存中是否已經存在該請求的響應,如果存在,則直接傳回緩存中的響應,否則,它可以将請求發送到伺服器并緩存伺服器的響應。
  6. 更新緩存:如果緩存中的資源發生了變化,Service Worker 可以自動更新緩存。例如,它可以在背景下載下傳最新的資源,并更新緩存中的檔案。

需要注意的是,使用 Service Worker 來緩存 http 請求資源需要一些額外的工作。例如,需要編寫 Service Worker 腳本來處理請求,并且需要将該腳本注冊到浏覽器中。此外,還需要考慮一些緩存政策,以確定緩存的資料與伺服器上的資料保持同步。

下面是一個使用 Service Worker 實作緩存的示例代碼:

// 注冊 Service Worker
if ('serviceWorker' in navigator) {
  window.addEventListener('load', function() {
    navigator.serviceWorker.register('/service-worker.js').then(function(registration) {
      console.log('ServiceWorker registration successful with scope: ', registration.scope);
    }, function(err) {
      console.log('ServiceWorker registration failed: ', err);
    });
  });
}

// 安裝 Service Worker
self.addEventListener('install', function(event) {
  console.log('ServiceWorker install');
  event.waitUntil(
    caches.open('my-cache').then(function(cache) {
      return cache.addAll([
        '/',
        '/index.html',
        '/styles.css',
        '/script.js',
        '/image.png'
      ]);
    })
  );
});

// 激活 Service Worker
self.addEventListener('activate', function(event) {
  console.log('ServiceWorker activate');
});

// 攔截網絡請求
self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request).then(function(response) {
      if (response) {
        console.log('ServiceWorker fetch from cache:', event.request.url);
        return response;
      } else {
        console.log('ServiceWorker fetch from network:', event.request.url);
        return fetch(event.request);
      }
    })
  );
});

// 更新緩存
self.addEventListener('activate', event => {
  event.waitUntil(
    caches.keys().then(cacheNames => {
      return Promise.all(
        cacheNames.filter(cacheName => {
          return cacheName.startsWith('my-cache') &&
            cacheName !== 'my-cache';
        }).map(cacheName => {
          return caches.delete(cacheName);
        })
      );
    })
  );
});           

當網絡請求到來時,會首先在緩存中查找對應的資源,如果有則直接傳回緩存中的資源,否則從網絡中擷取資源并傳回。這樣就可以實作基本的離線緩存功能。

在這個示例中,當 Service Worker 被安裝時,我們打開一個新的緩存并将應用程式的靜态資源添加到緩存中。在 fetch 事件中,我們攔截每個網絡請求并嘗試比對它們到我們的緩存中,如果比對到了則傳回緩存的響應,否則通過 fetch 方法從網絡中擷取資源。在 activate 事件中,我們可以更新緩存,删除舊的緩存項并将新的緩存項添加到緩存中。

繼續閱讀