前端性能毫秒必爭方案(二)HTTP緩存

web最好的體驗是不須要與服務器通訊

響應消除全部的網絡延遲,並避免數據傳輸的數據開銷。 爲了實現這一目標,HTTP規範容許服務器返回多個不一樣的緩存控制指令來控制如何,以及多長時間能夠經過瀏覽器和其餘中間緩存緩存單個響應。javascript

0.0.1. 好在每一個現代瀏覽器都自帶了 HTTP 緩存實現功能。

您只須要確保每一個服務器響應都提供正確的 HTTP 標頭指令,以指示瀏覽器什麼時候能夠緩存響應以及能夠緩存多久。php

0.0.2. 做爲開發者,怎麼利用HTTP Cache?

瀏覽器會替咱們完成全部工做,它會自動檢測以前是否指定了驗證令牌,它會將驗證令牌追加到發出的請求上,而且它會根據從服務器接收的響應在必要時更新緩存時間戳。java


0.0.3. 惟一要作的

就是確保服務器提供必要的 ETag 令牌。檢查您的服務器文檔中有無必要的配置標誌。**webpack

0.0.4. 送上Ngix 實戰配置,不玩虛的


# 爲JS 文件添加長時間緩存
location ~* \.js$ {
    add_header "section" "long expire"; #  僅供說明
    add_header Cache-Control "max-age=31536000";
}

# 或者爲JS文件刪除ETags
location ~* \.js$ {
  add_header "section" "no etags"; #  僅供說明
  etag off;
}

  location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
    error_page 404 = /index.php;
    expires 1M;
    access_log off;
}
//HTTP/1.1 200 OK
//Server: nginx
//Date: Thu, 23 Oct 2014 09:58:47 GMT
//Content-Type: application/javascript; charset=utf-8
//Content-Length: 1
//Last-Modified: Tue, 29 Oct 2013 15:17:17 GMT
//Connection: keep-alive
//ETag: "526fd17d-1"
//Cache-Control: max-age=31536000
//section: long expire
//Accept-Ranges: bytes

0.0.5. 慎!後面很長


HTTP 1.1 緩存校驗nginx

Last-Modified:根據最後修改時間匹配

Last-Modified 不是特別好,由於若是在服務器上,一個資源被修改了,但其實際內容根本沒發送改變,會由於Last-Modified時間匹配不上而返回了整個實體給客戶端 (即便客戶端緩存裏有個如出一轍的資源) 。也有可能服務器時間喝客戶端時間不一致也會出現問題web

瀏覽器請求會加上算法

If-Modified-Since: Last-Modified-value
或者 If-Unmodified-Since: Last-Modified-value瀏覽器

If-Modified-Since該請求首部告訴服務器若是客戶端傳來的最後修改時間與服務器上的一致,則直接回送304 和響應報頭便可緩存

If-Unmodified-Since告訴服務器,若Last-Modified沒有匹配上 (資源在服務端的最後更新時間改變了) ,則應當返回412(Precondition Failed) 狀態碼給客戶端服務器

ETag:根據資源標識符匹配

爲了解決上述Last-Modified可能存在的不許確的問題,Http1.1還推出了 ETag 實體首部字段。 服務器會經過某種算法,給資源計算得出一個惟一標誌符(好比md5標誌),服務器只須要比較客戶端傳來的ETag跟本身服務器上該資源的ETag是否一致,就能很好地判斷資源相對客戶端而言是否被修改過了。

若是服務器發現ETag匹配不上,那麼直接以常規GET 200回包形式將新的資源 (固然也包括了新的ETag) 發給客戶端;若是ETag是一致的,則直接返回304知會客戶端直接使用本地緩存便可。

瀏覽器請求會加上 If-None-Match: ETag-value 或者 If-Match: ETag-value

0.0.6. 若是 Last-Modified 和 ETag 同時被使用,則要求它們的驗證都必須經過纔會返回304,若其中某個驗證沒經過,則服務器會按常規返回資源實體及200狀態碼。

較新的 nginx 上默認是同時開啓了這兩個功能的 ,是否是感受本文白看了。


當服務器返回響應時 HtppResponseHeader會發出一組 HTTP 標頭,用於描述響應的內容類型、長度、緩存指令、驗證令牌等。

例如,在下圖的交互中,服務器返回一個 Gzip 壓縮過的 ,過期時間Expires 爲(Sat, 03 Jun 2017 08:45:24 GMT),如今是2017年5月27日。

HtppResponseHeader

開啓GZIP 減輕了近50%的數據請求

0.0.7. 經過 ETag 驗證緩存的響應

服務器使用 ETag HTTP 標頭傳遞驗證令牌。 驗證令牌可實現高效的資源更新檢查:資源未發生變化時不會傳送任何數據。

首次獲取資源 200 秒後,瀏覽器又對該資源發起了新的請求

瀏覽器會檢查本地緩存並找到以前的響應。

遺憾的是,該響應現已過時,瀏覽器沒法使用。

此時,瀏覽器能夠直接發出新的請求並獲取新的完整響應。 不過,這樣作效率較低,由於若是資源未發生變化,那麼下載與緩存中已有的徹底相同的信息就毫無道理可言!

這正是驗證令牌( ETag 標頭)旨在解決的問題。 服務器生成並返回的隨機令牌一般是文件內容的哈希值或某個其餘指紋。客戶端不須要了解指紋是如何生成的,只需在下一次請求時將其發送至服務器。若是指紋仍然相同,則表示資源未發生變化,您就能夠跳過下載。

If-None-Match 標頭 是瀏覽器本身自帶的默認行爲

在上圖中,客戶端自動在「If-None-Match」 HTTP 請求標頭內提供 ETag 令牌。服務器根據當前資源覈對令牌。若是它未發生變化,服務器將返回「304 Not Modified」響應,告知瀏覽器緩存中的響應未發生變化,能夠再延用 200 秒。請注意,您沒必要再次下載響應,這節約了時間和帶寬。

0.0.8. Cache-Control

1.每一個資源均可經過 Cache-Control HTTP 標頭定義其緩存策略 2.Cache-Control 指令控制誰在什麼條件下能夠緩存響應以及能夠緩存多久。

0.0.9. 注:

Cache-Control 標頭是在 HTTP/1.1 規範中定義的,取代了以前用來定義響應緩存策略的標頭(例如 Expires)。全部現代瀏覽器都支持 Cache-Control,所以,使用它就夠了。

0.0.10. max-age

指令指定從請求的時間開始,容許獲取的響應被重用的最長時間(單位:秒)。例如,「max-age=60」表示可在接下來的 60 秒緩存和重用響應。

0.0.11. no-cache

「no-cache」表示必須先與服務器確認返回的響應是否發生了變化,而後才能使用該響應來知足後續對同一網址的請求。所以,若是存在合適的驗證令牌 (ETag),no-cache 會發起往返通訊來驗證緩存的響應,但若是資源未發生變化,則可避免下載。

0.0.12. no-store

「no-store」則要簡單得多。它直接禁止瀏覽器以及全部中間緩存存儲任何版本的返回響應,例如,包含我的隱私數據或銀行業務數據的響應。每次用戶請求該資產時,都會向服務器發送請求,並下載完整的響應。

0.0.13. public

若是響應被標記爲「public」,瀏覽器和任何中繼緩存都可以將響應緩存下來,「public」不是必需的,由於明確的緩存信息(例如「max-age」)已表示響應是能夠緩存的。

0.0.14. private

瀏覽器能夠緩存「private」響應。不過,這些響應一般只爲單個用戶緩存,所以不容許任何中間緩存對其進行緩存。例如,用戶的瀏覽器能夠緩存包含用戶私人信息的 HTML 網頁,但 CDN 卻不能緩存。

0.0.15. 如何更新緩存的響應

瀏覽器發出的全部 HTTP 請求會首先路由到瀏覽器緩存,以確認是否緩存了可用於知足請求的有效響應。若是有匹配的響應,則從緩存中讀取響應,這樣就避免了網絡延遲和傳送產生的流量費用。

1.在資源「過時」以前,將一直使用本地緩存的響應。 2.您能夠經過在網址中嵌入文件內容指紋,強制客戶端更新到新版本的響應。

0.0.16. 實戰篇

假定您已告訴訪問者將某個 CSS 樣式表緩存長達 24 小時 (max-age=86400),但設計人員剛剛提交了一個您但願全部用戶都能使用的更新。您該如何通知擁有如今「已過期」的 CSS 緩存副本的全部訪問者更新其緩存?

在不更改資源網址的狀況下,作不到。這就涉及部署的問題。

只能緩存的版本將一直使用到過時或者用戶清除了瀏覽器緩存

0.0.17. 部署之緩存套路

您能夠在資源內容發生變化時更改它的網址,強制用戶下載新響應。一般狀況下,能夠經過在文件名中嵌入文件的指紋或版本號來實現 ,這其實就是涉及到網站部署的問題。咱們能夠採用非覆蓋式發佈。

名字+Hash值, 這些構建工具都能實現,好比webpack

這樣不但能夠控制每一個響應的緩存時間,還能夠控制訪問者看到新版本的速度。

HTML 被標記爲「no-cache」,這意味着瀏覽器在每次請求時都始終會從新驗證文檔,並在內容變化時獲取最新版本。這樣內嵌的資源發生改變都會從新請求。

CSS 設置爲 1 年後到期。請注意,您能夠放心地使用 1 年的「遠期過時」,由於您在文件名中嵌入了文件的指紋:CSS 更新時網址也會隨之變化。

JavaScript 一樣設置爲 1 年後到期,但標記爲 private,這或許是由於它包含的某些用戶私人數據是 CDN 不該緩存的。

圖像緩存時不包含版本或惟一指紋,並設置爲 1 天后到期。

您能夠組合使用 ETag、Cache-Control 和惟一網址來實現一舉多得:較長的過時時間、控制能夠緩存響應的位置以及隨需更新。

本文部分借鑑 https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching?hl=zh-cn

相關文章
相關標籤/搜索