天天看點

使用 HTTP 緩存防止不必要的網絡請求

原文

通過網絡擷取資源既緩慢又昂貴:

大型響應需要浏覽器和伺服器之間的多次往返。

直到所有關鍵資源都完全下載下傳後,您的頁面才會加載。

如果一個人使用有限的移動資料計劃通路您的網站,那麼每個不必要的網絡請求都是在浪費他們的錢。

如何避免不必要的網絡請求? 浏覽器的 HTTP 緩存是您的第一道防線。 它不一定是最強大或最靈活的方法,它對緩存響應的生命周期的控制有限,但它是有效的,所有浏覽器都支援它,并且不需要太多工作。

實際上沒有一個稱為 HTTP 緩存的 API。 它是 Web 平台 API 集合的總稱。 所有浏覽器都支援這些 API:

Cache-Control

ETag

Last-Modified

HTTP cache 的工作原理

浏覽器發出的所有 HTTP 請求都首先路由到浏覽器緩存,以檢查是否存在可用于滿足請求的有效緩存響應。如果比對,則從緩存中讀取響應,進而消除網絡延遲和傳輸産生的資料成本。

HTTP 緩存的行為由請求标頭和響應标頭的組合控制。在理想情況下,您可以控制 Web 應用程式的代碼(将确定請求标頭即 HTTP request headers)和 Web 伺服器的配置(将确定響應标頭即 HTTP response headers)。

Request headers: stick with the defaults (usually)

雖然有許多重要的标頭應該包含在您的 Web 應用程式的傳出請求中,但浏覽器在送出請求時幾乎總是代表您進行設定。 影響檢查新鮮度的請求标頭,如 If-None-Match 和 If-Modified-Since 隻是基于浏覽器對 HTTP 緩存中目前值的了解而出現。

這是個好消息——這意味着您可以繼續在 HTML 中包含諸如下列例子之類的标簽,并且浏覽器會自動為您處理 HTTP 緩存,而無需額外的努力。

使用 HTTP 緩存防止不必要的網絡請求

1

Response headers: configure your web server

HTTP 緩存設定中最重要的部分是您的 Web 伺服器添加到每個傳出響應的标頭。 以下标頭都會影響有效的緩存行為:

Cache-control: 伺服器可以傳回一個 Cache-Control 指令來指定浏覽器和其他中間緩存應該如何緩存單個響應以及緩存多長時間。

ETAG: 當浏覽器發現過期的緩存響應時,它可以向伺服器發送一個小令牌(通常是檔案内容的哈希)以檢查檔案是否已更改。 如果伺服器傳回相同的令牌,則檔案是相同的,無需重新下載下傳。

Last-Modified: 此标頭與 ETag 的用途相同,但使用基于時間的政策來确定資源是否已更改,這與 ETag 的基于内容的政策相反。

預設情況下,某些 Web 伺服器内置支援設定這些标頭,而其他 Web 伺服器則完全保留标頭,除非您明确配置它們。 如何配置标頭的具體細節因您使用的 Web 伺服器而異,您應該查閱伺服器的文檔以獲得最準确的詳細資訊。

省略 Cache-Control 響應标頭不會禁用 HTTP 緩存! 相反,浏覽器有效地猜測哪種類型的緩存行為對給定類型的内容最有意義。 您可能需要更多的控制權,是以請花時間配置您的響應标頭。

在配置 Web 伺服器的響應标頭時,您應該涵蓋兩個重要的場景。

Long-lived caching for versioned URLs

假設您的伺服器訓示浏覽器将 CSS 檔案緩存 1 年(Cache-Control: max-age=31536000),但您的設計人員剛剛進行了一個需要立即推出的緊急更新。您如何通知浏覽器更新檔案的“陳舊”緩存副本?你不能,至少不能不改變資源的 URL。在浏覽器緩存響應後,緩存版本将一直使用,直到它不再是最新的,這由 max-age 或 expires 決定,或者直到它因某些其他原因從緩存中被驅逐——例如,使用者清除他們的浏覽器緩存。是以,在建構頁面時,不同的使用者最終可能會使用不同版本的檔案:剛剛擷取資源的使用者使用新版本,而緩存較早(但仍然有效)副本的使用者使用其舊版本回複。您如何獲得兩全其美:用戶端緩存和快速更新?您更改資源的 URL 并強制使用者在其内容更改時下載下傳新響應。通常,您可以通過在檔案名中嵌入檔案的指紋或版本号來實作這一點,例如 style.x234dff.css。

當響應包含“指紋”或版本資訊且其内容永遠不會改變的 URL 請求時,請将 Cache-Control: max-age=31536000 添加到您的響應中。

設定這個值告訴浏覽器,當它需要在接下來的一年内的任何時候(31,536,000 秒;支援的最大值)加載相同的 URL 時,它可以立即使用 HTTP 緩存中的值,而無需向網絡送出請求 你的網絡伺服器。 太好了 - 您立即獲得了避免網絡帶來的可靠性和速度!

像 webpack 這樣的建構工具可以自動化将哈希指紋配置設定給資産 URL 的過程。

Server revalidation for unversioned URLs

不幸的是,并非您加載的所有 URL 都是版本化的。

也許您無法在部署 Web 應用程式之前包含建構步驟,是以您無法向資産 URL 添加哈希值。 并且每個 Web 應用程式都需要 HTML 檔案——這些檔案(幾乎!)永遠不會包含版本資訊,因為如果他們需要記住要通路的 URL 是

https://example.com

,那麼沒有人會費心使用您的 Web 應用程式 /index.34def12.html。 那麼你能對這些 URL 做些什麼呢?

這是您需要承認失敗的一種情況。 單獨的 HTTP 緩存不足以完全避開網絡 request. 但是你可以采取一些步驟來確定網絡請求盡可能快, 并盡可能高效。

以下 Cache-Control 值可以幫助您微調未版本控制的 URL 的緩存位置和方式:

no-cache: 這會訓示浏覽器每次使用 URL 的緩存版本之前都必須與伺服器重新驗證。

no-store: 這會訓示浏覽器和其他中間緩存(如 CDN)從不存儲檔案的任何版本。

private: 浏覽器可以緩存檔案,但中間緩存不能。

public: 響應可以由任何緩存存儲。

Etag 使用例子

使用 HTTP 緩存防止不必要的網絡請求

浏覽器從伺服器請求 /file 并包含 If-None-Match 标頭以訓示伺服器僅在伺服器上檔案的 ETag 與浏覽器的 If-None-Match 值不比對時才傳回完整檔案。 在這種情況下,這 2 個值确實比對,是以伺服器傳回 304 Not Modified 響應,并說明檔案應緩存多長時間(Cache-Control: max-age=120)。

總結

HTTP Cache 是一種提高負載性能的有效方式,因為它減少了不必要的網絡請求。 它在所有浏覽器中都受支援,并且不需要太多設定。

以下 Cache-Control 配置是一個好的開始:

Cache-control: no-cache - 對于每次使用前應與伺服器重新驗證的資源

Cache-control:no-store: - 不應該緩存的資源

Cache-control: max-age=31536000:版本化資源