1. 程式人生 > >寫給前端的http快取詳解

寫給前端的http快取詳解

http提供了非常強大的快取機制,關於http前端詳解看上一篇

文章分為三部分,我們先來統一梳理一下一個快取請求的過程,然後從請求頭以及響應頭快取相關欄位進行解析,最後總結一下前端需要了解的對於快取的操作

一 . 快取過程:

當一個使用者發起一個靜態資源請求的時候,瀏覽器會通過以下幾步來獲取資源

當第一次傳送請求,http返回200的狀態碼,

如果沒有關閉快取請求的話(沒標明不使用快取,下述)則會在返回頭中返回包含last-Modified以及Etag和Expires的欄位(這些欄位下面慢慢說),然後將檔案儲存在Cache目錄下;

當後續請求該檔案時候,先在本地查詢該資源,如果在本地快取找到對應的資源,但是不知道該資源是否過期或者已經過期, 則發一個http請求到伺服器,然後伺服器判斷這個請求,

如果請求的資源在伺服器上沒有改動過,則返回304, 讓瀏覽器使用本地找到的那個資源

而如果當伺服器發現請求的資源已經修改過,或者這是一個新的請求(本地無對應資源),伺服器則返回該資源的資料,並且返回200,

當然這個是指找到資源的情況下,如果伺服器上沒有這個資源,則返回404

經過上面的流程梳理,我們基本瞭解整個快取處理過程,不過對於前端來說,我們需求無非就是使用快取或者不使用快取,在瞭解下面內容之後我們再根據原理去慢慢實現我們的需求。

二 . http頭部快取相關key:

首先我們要先根據http請求頭以及響應頭 先看一些跟快取相關的報文頭cache-control, if-none-match, if-modified-since, Etag,expires, last-modified,

這些在上一篇文章HTTP前端詳解中也提及過留在這篇文章進行分析;

request header快取相關:

cache-control:

其快取指令對於前段常用的有如下no-cache、no-store、max-age這幾個值;分別來說一下

no-cache:

表面意為“資料內容不被快取”,而實際資料是被快取到本地的,只是每次請求時候直接繞過快取這一環節直接向伺服器請求最新資源,由於瀏覽器解釋不一樣,

例如ie中我們設定了no-cache之後,請求雖然不會直接使用快取,但是還會用快取資料與伺服器資料進行一致性檢測(也就是說還是有機率會用到快取的),

firefox中則完全無視no-cache存在,詳細解釋見no-store;

no-store:

指示快取不儲存此次請求的響應部分。與no-cache比較來說,一個是不用快取,一個是不儲存快取;按理來說這個設定更加粗暴直接禁用快取,

但是具體實現起來 瀏覽器之間差異卻特別大,一般不會直接用該欄位進行設定,不過no-store是為了防止快取被惡意修改儲存路徑導致資訊被洩露而設定的,

畢竟有它的用處,在firefox中實現快取是通過檔案另存為將快取副本儲存到本地,直接利用no-cache對其是無效的,如果加上no-store設定的話 則可以起到與no-cache一樣的效果;

即:cache-control:no-cache,no-store;可以確保在支援http1.1版本中各大瀏覽器回車後退重新整理無快取;

再加上Pragma: no-cache設定相容版本1.0即可(不過為了防止一致性檢測時候的萬一我們還是最好加上一致性檢測的內容,如下所示幾種方式);

max-age:

例如Cache-control: max-age=3;表示此次請求成功後3秒之內傳送同樣請求不會去伺服器重新請求,而是使用本地快取;同樣我們如果設定max-age=0表示立即拋棄快取直接傳送請求到伺服器

一致性檢測分為兩種方式:1.檢測日期是否過期,檢測資源是否更新;

if-none-match:

該欄位與響應中的eTag一起使用,表示檢查實體是否有更新改變;客戶端第一次傳送請求時候響應報文會包含欄位Etag,表示資源狀態,當資源改變後該值也會改變(客戶端不必關心該值怎麼生成)

然後快取儲存下該欄位,第二次已經有該快取時候在瀏覽本地快取時候會將該值賦給if-none-match欄位傳送給伺服器,伺服器將傳送的值與當前的狀態進行對比,

如果值一樣的話則答覆304去使用快取資料,如果值改變了則傳送最新資料給客戶端替代現有快取資料,並且返回狀態200;

if-modified-since:

該欄位與last-modified配合使用,跟上述原理差不多,都是響應端先返回一個last-modified時間欄位,再次請求時候 request頭部會將快取中的last-modified欄位拿出來賦給if-modified-since,

傳送給伺服器,伺服器去判斷時間是否過期,如未過期則返回304,告訴客戶使用快取資料,如果過期則重新返回一個last-modified並且返回200;

repsonse header快取相關:

Etag:

剛才也說過 是跟if-none-match配合去使用,它根據實體內容生成的一段hash字串(類似於MD5或者SHA1之後的結果),可以標識資源的狀態。 當資源傳送改變時,ETag也隨之發生變化。

使用Etag主要是為了解決根據時間無法解決的問題:比如檔案修改頻繁(秒之內修改),導致根據時間無法判斷是否更新;以及修改時間變了,但是內容沒變(我們應該認為該檔案是沒變的)

expires:

表示快取過期時間例如:expires:Mon Dec 30 2011 11:01:19 GMT,跟cache-control中的max-age作用一樣,不過在碰見max-age之後,該值會被覆蓋從而被max-age替代;

last-modified:

表示檔案最後修改時間;

另外響應端的報文頭也有對cache-content的規定,與request大體相同,另有未提及的 歡迎大家補充

三 . 實現有關前端對於快取的操作:

使用快取:

預設情況下,瀏覽器都會使用快取資料,

在f5重新整理情況下 瀏覽器會發送一致性驗證去伺服器驗證是否使用快取,而瀏覽器直接回車則表示直接應用快取不需要去伺服器驗證;所以我們就按照f5重新整理去解釋實現使用快取:

一般來說前端預設是使用快取的,預設情況下伺服器端以及前端都會使用快取資料,或者是根據etag或者是根據max-age或者是根據expires根據伺服器不同去不同實現;

實際中大部分不需要我們手動去實現,而有些我們不確定是否使用快取的情況下我們可以手動加以干涉強制使用快取資料:

例如某個靜態檔案包括html或者圖片我們需要使用快取來提高處理速度,

方法一:

在伺服器進行配置其max-age或者expires使其設定一個過期值為當前一年之後。這樣每次進行檢驗時候都會使用快取中檔案.例如在.htaccess中

<IfModule mod_headers.c>

 <FilesMatch ".(gif|jpg|jpeg|png|ico)$">

Header set Cache-Control "max-age=604800"

 </FilesMatch>

方法二:

前端設定if-modified-since去設定一個上次修改時間大於當前日期,

方法三:

伺服器端根據etag去判斷是否匹配來根據實際業務來使用快取;

後面兩個方法屬於弱快取資料頭,需要浪費http連線,所以建議使用第一種方式;

禁用快取:

方法一:

可以在meta標籤標明<meta http-equiv="pragma" content="no-cache">

<meta http-equiv="cache-control" content="no-cache">

<meta http-equiv="expires" content="0"> 

方法二:

也可以動態去setRequestHeader,強制不用快取設定組合如下:

cache-control='no-cache,no-store'

pragma='no-cache'

if-modified-since=0;

方法三:

請求端設定if-modified-since為已經過期的某個時間,可以是幾年前或者幾十年前。

方法四:

服務端設定Expires為過期某個時間,例如php中header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");

實際開發中如果需要一致性檢測則儘量去配合Etag以及last-Modified去進行比較然後返回使用快取還是新資料;這個有點偏伺服器端了,不再贅述

方法五:

url後面加隨機數或者時間戳url += “&random=” + Math.random()這個方法js以及php經常用,原理就是每個請求的url都不一樣這樣一來快取中找不到對應資料,就自動去伺服器尋找最新資源;

最後回到上篇開始提到一個問題;請求伺服器時間,由於快取導致時間返回問題:

問題回顧:由於沒有設定快取,預設瀏覽器是讀取快取資料的,導致我沒清快取情況下一直使用快取資料,這樣一來我就取不到真正的當前伺服器時間了。

解決方式:利用上述方法二+方法四,動態在請求時候修改header以及根據隨機數去請求最新資料,問題解決!!!

總結:

關於快取 這篇文章有點高開低走的感覺,實際問題是可以通過各種手段解決的,例如google或者諮詢大牛,但是原理還是應該掌握的,

因為關於某一方面的問題不僅僅工作中只遇到這一次而且還是一樣的問題,自己掌握了,才能更好的去處理解決問題。

寫於2013年倒數第二天,快要跨年了,預祝大家新年快樂。。