針對web開發者的瀏覽器緩存指南(譯)

原文連接javascript

我的文章彙總在blogcss

概要

緩存是一個很是重要一樣也很是複雜的瀏覽器特性html

在這篇文章中,咱們將解釋瀏覽器是如何利用緩存來使加載頁面更快,哪些因素決定了緩存的週期,在必要的時候如何去避開緩存。html5

爲何緩存會如此重要呢?

全部的瀏覽器都會嘗試去緩存靜態資源的本地副本,以此去下降頁面的加載時間以及體積最小的網絡傳輸java

不論服務是在相同的網絡環境下仍是在世界上其餘的網絡環境下,從本地緩存中取資源必定會比經過網絡請求的方式要快git

如何讓瀏覽器緩存生效呢?

case1:用戶以前沒有訪問過這個站點

瀏覽器針對此站點沒有任何的緩存文件,因此瀏覽器會向站點服務去請求全部所需的資源github

image

下面是一張首次訪問維基百科首頁的資源下載完以後的截圖,底部狀態欄顯示出有265kb的數據傳輸到了瀏覽器中web

image

case2:用戶以前訪問過站點

瀏覽器會去請求站點服務器取到html頁面,而後會查看是否有對應靜態資源的緩存(js, css, images)chrome

image

當咱們從新刷新維基百科的頁面時,就能看出由於緩存的緣由與以前的請求情況不一樣的地方瀏覽器

image

此時數據的傳輸量已經降到了928bytes,至關於首頁訪問頁面時的0.3%,Size這一列就顯示出了咱們的大多數的資源都來自於緩存

Chrome即會在memory cache中去拉取內容,也會在disk cache中去拉取內容,由於在case1和case2中,咱們尚未關閉瀏覽器,因此數據依然會存儲在memory cache中

顯示瀏覽器緩存

在chrome中,咱們能夠在地址欄中輸入chrome://cache去查看緩存的內容,對於每個已經緩存的文件,這裏都將會顯示一個頁面連接,頁面連接的內容包含一個更加詳細的視圖說明。

image

瀏覽器是如何知道什麼該緩存的

瀏覽器先去檢查服務器所生成的http響應頭信息,通常用於緩存相關的頭信息有4個:

  • ETag
  • Cache-Control
  • Expires
  • Last-Modified

ETag

ETag(Entity Tag)是一個用於緩存校驗token的字符串,它一般以文件的哈希值來表示

服務器能夠添加ETag到它的響應裏,瀏覽器接收到這個響應,那在將來的請求中(在文件過時以後),瀏覽器就能夠根據這個字段去判斷緩存中是否保留着一份過時的副本

若是經過對比hash值是相同的,那麼就說明這個資源沒有變化,服務器就會返回一個304狀態碼(Not Modified),瀏覽器就知道當前緩存的副本是安全的

iamge

注意:只有當緩存中的文件過時了,ETag纔會被用在請求中

Cache-Control

Cache-Control頭擁有許多指令,利用這些指令咱們能夠設置緩存的行爲,過時時間,驗證等,同時這些也能夠組合起來一塊兒進行設置。

Cache Behavior(緩存行爲)
Cache-Control: public
複製代碼

public意味着資源能夠被任意緩存(瀏覽器,CDN等)

Cache-Control: private
複製代碼

private意味着資源只能被瀏覽器緩存

Cache-Control: no-store
複製代碼

no-store意味着讓瀏覽器老是去請求服務器以獲取資源

Cache-Control: no-cache
複製代碼

no-cache有一點誤導性,它並非說「不要緩存」

這是在告訴瀏覽器去緩存這個文件,但在和服務器確認最新版本以前不要去使用它。這個驗證的過程是經過ETag來完成的

這個行爲通常會用在html文件中,由於瀏覽器總會去檢查最新的html文件標識,因此這一點是講得通的

Expiration
Cache-Control: max-age=60
複製代碼

這個指令指定了資源應該被緩存多少秒,因此max-age=60就意味着資源應該被緩存一分鐘,RFC 2616推薦這裏的最大值不要超過一年(max-age=31536000)

Cache-Control: s-max-age=60
複製代碼

這個指令僅僅是被用在中間緩存(好比CDN)

Validation
Cache-Control: must-revalidate
複製代碼

這個指令告訴緩存在使用它以前必須去驗證資源的過時狀態,若是已通過期就不該該被使用

Expires

Expires頭部來源於http1.0版本,可是在當今許多站點中依然保留着

這個頭部字段指定了一個日期,超過這個日期就表明資源是無效的

Expires: Wed, 25 Jul 2018 21:00:00 GMT
複製代碼

注意:若是已經指定了Cache-Control中的max-age指令,那瀏覽器就會忽略expires

Last-Modified

Last-Modified頭部也是來自http1.0版本

Last-Modified: Mon, 12 Dec 2016 14:45:00 GMT
複製代碼

這個字段包含了資源最後修改的日期和時間

HTML Meta Tag

在HTML5版本以前,在html中使用元標籤(meta tag)制定cache-control是一個有效的方式

<meta http-equiv="Cache-control" content="no-cache">
複製代碼

但在html5中已經不推薦這麼作了,爲何?由於只有瀏覽器能夠識別這個標籤,而中間緩存(CDN)是識別不了的。

因此最好是都經過http頭的方式去發送緩存指令

HTTP Response

讓咱們來看一個簡單的http響應

Accept-Ranges: bytes
  Cache-Control: max-age=3600
  Connection: Keep-Alive
  Content-Length: 4361
  Content-Type: image/png
  Date: Tue, 25 Jul 2017 17:26:16 GMT
  ETag: "1109-554221c5c8540"
  Expires: Tue, 25 Jul 2017 18:26:16 GMT
  Keep-Alive: timeout=5, max=93
  Last-Modified: Wed, 12 Jul 2017 17:26:05 GMT
  Server: Apache
複製代碼
  • 第2行告訴咱們max-age爲1個小時
  • 第5行告訴咱們這是一個png圖片資源
  • 第7行告訴咱們ETag將在1個小時以後去驗證資源是否改變過
  • 第8行,Expires頭會被忽略,由於已經設置了Cache-Control: max-age=3600
  • 第10行,Last-Modified頭展現了圖片資源的最後修改時間

緩存的誤區

因此咱們已經意識到緩存是個好東西,咱們應該積極利用它

可是咱們但願的是,不須要讓用戶每次都去刷新(Ctrl + F5)或者清空緩存才能看到咱們頁面的最新內容

這類緩存問題常常困擾者開發者以及用戶,用戶可能會看到一個錯亂的頁面或者是一個行爲怪異的按鈕,由於他們當前使用的已是過時的靜態資源文件

過時的文件(Stale Files)

下面這張截圖描述了一個銀行網站的用戶與Chase Support之間的交流,能夠看到是有關登錄表單出了問題

image

該用戶頗有可能還在使用老的js文件,這就是致使點擊登錄按鈕是表單重置操做而不是表單提交操做

讓咱們來探索過時文件影響咱們的另一種狀況。

假設咱們在一個叫作app.min.js的文件中修復了一個bug,而且把它推送到了生產環境

在HTML中,該腳本是這個樣子

<script src="assets/js/app.min.js">
複製代碼

咱們的web服務器給這個js文件設置了max-age爲1周(604800秒)的過時時間

Cache-Control: private, max-age=604800
複製代碼

在更新完該js文件以後,一些用戶仍在反饋說那個bug依然存在。 那這裏到底發生了什麼呢?

  • Bob兩個星期之前訪問了這個網站而且緩存一個有問題的app.min.js文件的副本,因爲他的副本已通過期,因此瀏覽器回去請求服務器,去拿最新的已經修復了bug的版本文件。
  • Mary在兩天以前訪問了該網站,一樣緩存了一個有問題的app.min.js文件的副本,她的副本因爲還沒過時,因此瀏覽器仍是會去使用緩存中的那個副本

在下一個章節中,咱們將會利用一個叫作"cache busting"的技術來解決這些問題和狀況

Cache Busting

Cache busting會使一個資源文件失效,強制瀏覽器去服務器端從新獲取數據。

咱們能夠經過改變文件名來命令瀏覽器去避開緩存,對於瀏覽器來講,這是一份徹底新的資源,因此瀏覽器會去服務端請求最新的數據

Cache busting一樣容許咱們設置一個比較長的max-age值針對頻繁改動的資源。Google推薦max-age被設爲1年(source

版本號

咱們能夠給文件名添加一個版本號

assets/js/app-v2.min.js
複製代碼

指紋(finerprinting)

咱們能夠基於文件內容添加一個指紋值

assets/js/app-d41d8cd98f00b204e9800998ecf8427e.min.js
複製代碼

拼接查詢參數

咱們能夠在文件名的末尾拼接一個查詢參數

assets/js/app.min.js?version=2
複製代碼

拼接查詢參數的方式在和代理服務器交互時有明顯的錯誤(known issues),因此這種方式通常不推薦使用

最佳實踐

推薦

  • 對於靜態資源來講,使用Cache-Control和ETag頭部來控制緩存的行爲
  • 設置一個比較長的max-age值,以此來得到瀏覽器緩存帶來的好處
  • 針對Cache busting,使用指紋值(或hash值)和版本號的方式

不推薦

  • 使用html元標籤去指定緩存的行爲
  • 使用查詢參數的方式實現Cache busting

FAQ

如何知道這個文件時來自於緩存中

打開web開發者工具,在chrome中,這個信息會顯示在network面板中的Size列

如何阻止去緩存一個文件

使用下面的響應頭

Cache-Control: no-cache, no-store, must-revalidate
複製代碼

注意

從chrome 66版本開始,chrome已經刪除了chrome://cache,緣由能夠參考reason1, reason2

相關文章
相關標籤/搜索