通常所說的web緩存指的是可以自動儲存常見http請求副本的http裝置。對于前端開發者來說,浏覽器充當了重要角色。除此外常見的還有各種各樣的代理伺服器也可以做緩存。當web請求到達緩存時,緩存從<code>本地副本</code>中提取這個副本内容而不需要經過伺服器。這帶來了以下優點:
緩存減少了備援的資料傳輸,節省流量
緩存緩解了帶寬瓶頸問題。不需要更多的帶寬就能更快加載頁面
緩存緩解了瞬間擁塞,降低了對原始伺服器的要求。
緩存降低了距離延時, 因為從較遠的地方加載頁面會更慢一些。
緩存可以是單個使用者專用的,也可以是多個使用者共享的。專用緩存被稱為<code>私有緩存</code>,共享的緩存被稱為<code>公有緩存</code>。
私有緩存隻針對專有使用者,是以不需要很大空間,廉價。web浏覽器中有内建的私有緩存——大多數浏覽器都會将常用資源緩存在你的個人電腦的磁盤和記憶體中。如chrome浏覽器的緩存存放位置就在:<code>c:\users\your_account\appdata\local\google\chrome\user data\default</code>中的cache檔案夾和media cache檔案夾。
公有緩存是特殊的共享代理伺服器,被稱為<code>緩存代理伺服器</code>或<code>代理緩存</code>(反向代理的一種用途)。公有緩存會接受來自多個使用者的通路,是以通過它能夠更好的減少備援流量。
下圖中每個用戶端都會重複的向伺服器通路一個資源(此時還不在私有緩存中),這樣它會多次通路伺服器,增加伺服器壓力。而使用共享的公有緩存時,緩存隻需要從伺服器取一次,以後不用再經過伺服器,能夠顯著減輕伺服器壓力。

事實上在實際應用中通常采用階層化的公有緩存,基本思想是在靠近用戶端的地方使用小型廉價緩存,而更高層次中,則逐漸采用更大、功能更強的緩存在裝載多使用者共享的資源。
而對于前端開發者來說,我們主要跟浏覽器中的緩存打交道,是以上圖流程簡化為:
下面這張圖展示了某一網站,對不同資源的請求結果,其中可以看到有的資源直接從緩存中讀取,有的資源跟伺服器進行了再驗證,有的資源重新從伺服器端擷取。
注意,我們讨論的所有關于緩存資源的問題,都僅僅針對<code>get</code>請求。而對于<code>post</code>, <code>delete</code>, <code>put</code>這類行為性操作通常不做任何緩存
http通過緩存将伺服器資源的副本保留一段時間,這段時間稱為<code>新鮮度限值</code>。這在一段時間内請求相同資源不會再通過伺服器。http協定中<code>cache-control</code> 和 <code>expires</code>可以用來設定新鮮度的限值,前者是http1.1中新增的響應頭,後者是http1.0中的響應頭。二者所做的事時都是相同的,但由于<code>cache-control</code>使用的是相對時間,而expires可能存在用戶端與伺服器端時間不一樣的問題,是以我們更傾向于選擇<code>cache-control</code>。
下面我們來看看<code>cache-control</code>都可以設定哪些屬性值:
max-age(機關為s)指定設定緩存最大的有效時間,定義的是時間長短。當浏覽器向伺服器發送請求後,在max-age這段時間裡浏覽器就不會再向伺服器發送請求了。
當在5秒内第二次通路頁面時,浏覽器會直接從緩存中取得資源
public 指定響應可以在代理緩存中被緩存,于是可以被多使用者共享。如果沒有明确指定private,則預設為public。
private 響應隻能在私有緩存中被緩存,不能放在代理緩存上。對一些使用者資訊敏感的資源,通常需要設定為private。
no-cache 表示必須先與伺服器确認資源是否被更改過(依靠<code>if-none-match</code>和<code>etag</code>),然後再決定是否使用本地緩存。
如果上文中關于<code>cache.png</code>的處理改成下面這樣,則每次通路頁面,浏覽器都需要先去伺服器端驗證資源有沒有被更改。
no-store 絕對禁止緩存任何資源,也就是說每次使用者請求資源時,都會向伺服器發送一個請求,每次都會下載下傳完整的資源。通常用于機密性資源。
<code>cache-control</code>不僅僅可以在響應頭中設定,還可以在請求頭中設定。浏覽器通過請求頭中設定<code>cache-control</code>可以決定是否從緩存中讀取資源。這也是為什麼有時候點選浏覽器重新整理按鈕和在位址欄回車,在network子產品中看到完全不同的結果
不推薦使用expires,它指定的是具體的過期日期而不是秒數。因為很多伺服器跟用戶端存在時鐘不一緻的情況,是以最好還是使用<code>cache-control</code>.
浏覽器或代理緩存中緩存的資源過期了,并不意味着它和原始伺服器上的資源有實際的差異,僅僅意味着到了要進行核對的時間了。這種情況被稱為伺服器再驗證。
如果資源發生變化,則需要取得新的資源,并在緩存中替換舊資源。
如果資源沒有發生變化,緩存隻需要擷取新的響應頭,和一個新的過期時間,對緩存中的資源過期時間進行更新即可。
http1.1推薦使用的驗證方式是<code>if-none-match</code>/<code>etag</code>,在http1.0中則使用<code>if-modified-since</code>/<code>last-modified</code>。
上文的demo中我們見到過伺服器端如何驗證etag:
由于etag有伺服器構造,是以在叢集環境中一定要保證etag的唯一性
這兩個是http1.0中用來驗證資源是否過期的請求/響應頭,這兩個頭部都是日期,驗證過程與<code>etag</code>類似,這裡不詳細介紹。使用這兩個頭部來驗證資源是否更新時,存在以下問題:
有些文檔資源周期性的被重寫,但實際内容沒有改變。此時檔案中繼資料中會顯示檔案最近的修改日期與<code>if-modified-since</code>不相同,導緻不必要的響應。
有些文檔資源被修改了,但修改内容并不重要,不需要所有的緩存都更新(比如代碼注釋)
本文demo代碼如下: