一、強制緩存
只要瀏覽器判斷緩存沒有過期,則直接使用瀏覽器的本地緩存而無需再請求服務器。
強制緩存是利用下面這兩個 HTTP 響應頭部(Response Header)字段實現的,它們都用來表示資源在客戶端緩存的有效期:
Cache-Control, 是一個相對時間;
Expires,是一個絕對時間;
如果 HTTP 響應頭部同時有 Cache-Control 和 Expires 字段的話,Cache-Control 的優先級高于 Expires 。
Cache-control 選項更多一些,設置更加精細,所以建議使用 Cache-Control 來實現強制緩存。具體的實現流程如下:
當瀏覽器第一次請求訪問服務器資源時,服務器會在返回這個資源的同時,在 Response 頭部加上 Cache-Control,Cache-Control 中設置了過期時間大小;
瀏覽器再次請求訪問服務器中的該資源時,會先通過請求資源的時間與 Cache-Control 中設置的過期時間大小,來計算出該資源是否過期,如果沒有,則使用該緩存,否則重新請求服務器;
服務器再次收到請求后,會再次更新 Response 頭部的 Cache-Control。
二、協商緩存
與服務端協商之后,通過協商結果來判斷是否使用本地緩存。
協商緩存可以基于兩種頭部來實現。
第一種:請求頭部中的 If-Modified-Since 字段與響應頭部中的Last-Modified字段
? ? ? ? ? ? ? ? (基于時間實現)
響應頭部中的 Last-Modified:標示這個響應資源的最后修改時間;
請求頭部中的 If-Modified-Since:當資源過期了,發現響應頭中具有 Last-Modified 聲明,則再次發起請求的時候帶上 Last-Modified 的時間,服務器收到請求后發現有 If-Modified-Since 則與被請求資源的最后修改時間進行對比(Last-Modified)。
如果最后修改時間較早,說明資源又被改過,則返回最新資源,HTTP 200 OK;
如果最后修改時間較小,說明資源無新修改,響應 HTTP 304 走緩存。
第二種:請求頭部中的 If-None-Match 字段與響應頭部中的 ETag 字段
? ? ? ? ? ? ? ? (基于一個唯一標識實現,能更準確地判斷文件內容是否被修改)
響應頭部中ETag:唯一標識響應資源;
請求頭部中的 If-None-Match:當資源過期時,瀏覽器發現響應頭里有 Etag,則再次向服務器發起請求時,會將請求頭 If-None-Match 值設置為 Etag 的值。服務器收到請求后進行比對,如果資源沒有變化返回 304,如果資源變化了返回 200。
如果在第一次請求資源的時候,服務端返回的 HTTP 響應頭部同時有 Etag 和 Last-Modified 字段,那么客戶端再下一次請求的時候,如果帶上了 ETag 和 Last-Modified 字段信息給服務端,這時 Etag 的優先級更高,也就是服務端先會判斷 Etag 是否變化了,如果 Etag 有變化就不用在判斷 Last-Modified 了,如果 Etag 沒有變化,然后再看 Last-Modified。
為什么 ETag 的優先級更高?
這是因為 ETag 主要能解決 Last-Modified 幾個比較難以解決的問題:
在沒有修改文件內容情況下文件的最后修改時間可能也會改變,這會導致客戶端認為這文件被改動了,從而重新請求;
可能有些文件是在秒級以內修改的,If-Modified-Since 能檢查到的粒度是秒級的,使用 Etag就能夠保證這種需求下客戶端在 1 秒內能刷新多次;
有些服務器不能精確獲取文件的最后修改時間。
注意,協商緩存這兩個字段都需要配合強制緩存中 Cache-Control 字段來使用,只有在未能命中強制緩存的時候,才能發起帶有協商緩存字段的請求。