瀏覽器的快取機制—強快取與協商快取

業精於勤,行成於思發表於2020-11-06

前言:

web中有些場景下很多內容是不需要更改的,如果每次請求都向伺服器請求那些一段時間內不會變動的內容資料,會造成不必要的頻寬浪費。

有時候網路較差時,請求這些內容就需要花費很長時間來開啟頁面。

因此通過瀏覽器的快取機制,協同伺服器讓瀏覽器快取那些不需要頻繁變動的資源就可以有效地降低流量消耗和響應時間。

一、什麼是http快取

http快取指的是:當客戶端向伺服器請求資源時,會先檢視瀏覽器快取,如果瀏覽器有“要請求資源”的副本,就可以直接從瀏覽器快取中提取,而不需要從伺服器中請求這個資源。

需要注意的是,常見的http快取只能快取GET請求的資源,所以下面說的請求快取皆是指GET請求。

http快取分類:根據是否需要向伺服器發起請求把http快取分為兩個大類,強快取協商快取

http快取都是從第二次請求開始的:

  • 第一次請求資源時,客戶端向伺服器請求資源,伺服器返回響應資源,並在response header中回傳資源的快取引數;
  • 第二次請求資源時,瀏覽器判斷這些請求引數,命中強快取就返回200,使用磁碟快取中的資源,不請求伺服器,否則就把請求引數加到request header中傳給伺服器,看是否命中協商快取,命中則返回304,使用快取資源,若都沒命中則伺服器會返回新的資源。

二、強快取

強快取是通過設定快取的到期時間 expires 或者有效時間 max-age ,在有效時間內,快取不會失效,瀏覽器直接從瀏覽器快取中讀取資源。當快取資料庫中沒有所請求的資源,或所請求的資源已失效時,才會從服務端請求資源。

與強制快取相關的請求響應頭: 

  • Expires

響應頭,代表該資源的過期時間。但由於服務端時間和客戶端時間可能有誤差,這也將導致快取命中可能有誤差,另一方面,Expires是HTTP1.0的產物,故現在大多數使用Cache-Control替代。

  • Cache-Control(優先順序高於Expires)

請求/響應頭,快取控制欄位,精確控制快取策略。 Cache-Control有很多屬性,不同的屬性代表的意義也不同。

  1. private:客戶端可以快取
  2. public:客戶端和代理伺服器都可以快取
  3. max-age=x:快取內容將在x秒後失效
  4. no-cache:需要使用協商快取來驗證快取資料
  5. no-store:所有內容都不會快取
  • pragma 

它的值有no-cache和no-store,表示意思同cacha-control,優先順序高於cache-control和expires,即三者同時出現時,先看pragma -> cache-control -> expires。

三、協商快取

協商快取需要在伺服器端對比資源是否修改,來判斷是否可以使用快取。若未改動,則返回304狀態碼,瀏覽器拿到此狀態碼就可以直接使用快取資料了。否則伺服器返回新資源。

由此可見,在協商快取中,如何判斷資源是否改動就尤為重要啦,現在主要有兩種策略:Last-Modified 和 Etag 

  • Last-Modified

伺服器在響應請求時,會告訴瀏覽器資源GMT格式的最後修改時間。

瀏覽器非第一次請求伺服器時,請求頭會包含 if-Modified-Since 欄位,後面跟著在快取中獲得的最後修改時間。服務端收到此請求頭發現有if-Modified-Since,則與被請求資源的最後修改時間進行對比,如果一致則返回304和響應報文頭,此時瀏覽器只需要從快取中獲取資源即可。 

  • 如果真的被修改:那麼開始傳輸響應一個整體,伺服器返回:200 OK
  • 如果沒有被修改:那麼只需傳輸響應header,伺服器返回:304 Not Modified

Last-Modified 其實用起來不一定完全準確:

  1. Last-Modified標註的最後修改只能精確到秒級,如果某檔案在1秒鐘以內被修改多次的話,它將不能準確標註檔案的修改時間
  2. 如果某些檔案被修改了,但是內容並沒有任何變化,而Last-Modified卻改變了,導致檔案沒法使用快取
  3. 有可能存在伺服器沒有準確獲取檔案修改時間,或者與代理伺服器時間不一致等情形

因此,HTTP1.1推出了Etag,改進了這些問題。

  • Etag(優先順序高於Last-Modified)

伺服器響應請求時,通過此欄位告訴瀏覽器當前資源在伺服器生成的唯一標識(生成規則由伺服器決定)。

非第一次請求伺服器時,瀏覽器的請求報文頭部(Request Headers)會包含 If-None-Match 欄位,後面的值為在快取中獲取的標識。伺服器接收到此報文後就用 If-None-Match 的值與被請求資源的唯一標識進行對比。

  • 不同,則說明資源被改動,則返回狀態碼200,伺服器返回新資源。
  • 相同,說明資源未修改,則返回狀態碼304,瀏覽器直接從快取中獲取資料資源。

不過etag也存在缺點,就是每次生成表示字串會增加伺服器的開銷。所以要如何使用last-modified和etag還需要根據具體需求進行權衡。

四、動動手

好了,理論部分說完了,下面我們自己動手嘗試一下

我用nodejs開了一個伺服器,用來獲取get請求的響應,並在其中設定http快取的相關屬性,網頁部分就是一個簡單的載入圖片,初學nodejs寫介面,沒有很完善,只是為了弄明白快取怎麼用,僅供參考。

強快取:

nodejs中get請求部分的程式碼:

 
  1. app.get("/api/getPic", (req, res) => {

  2. res.setHeader("Cache-Control", "public,max-age=120"); //max-age設定的2分鐘

  3. let date = new Date(Date.now() + 5000).toUTCString(); //Expires過期時間設定了5分鐘後

  4. res.setHeader("Expires", date);

  5. let data = JSON.stringify({

  6. msg: "請求成功",

  7. result: [

  8. {

  9. url:

  10. "https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=350525183,1430160676&fm=11&gp=0.jpg"

  11. }

  12. ]

  13. });

  14. res.send(data);

  15. });

傳送一次請求,可以看到強快取的配置已經設定好了(預設帶了協商快取的etag,但是我也不知道為什麼。。。。。。):

之後我立即重新整理頁面,此時變成醬紫了,status Code是200,後邊顯示from disk cache,此時的圖片資源即從磁碟快取中獲取,不請求伺服器:

 兩分鐘後,再次重新整理頁面,此時status code顯示304,即請求的伺服器,請求的資源沒有被更改,命中的協商快取:

由於預設帶了 Etag協商快取,所以協商快取部分就沒有手寫,感興趣的小夥伴可自行編寫設定 Last-Modified 和 Etag 屬性值。

補充:

網路請求的size會出現三種情況

  1. from memory cache (記憶體快取)
  2. from disk cache (磁碟快取)
  3. 資源數值大小

  1. 顯示數值的狀態碼為200, 直接從伺服器下載最新資源
  2. from memory cache 不請求網路資源,資源在記憶體當中,一般js指令碼,字型,圖片會存放在記憶體當中
  3. from disk cache 不請求網路資源,在磁碟當中,一般非指令碼會存在記憶體當中,如css

五、不同的網頁重新整理操作

我們將訪問和重新整理分為以下三種情況:

  • 標籤進入、輸入url回車進入:按照指定的快取策略去操作
  • 按重新整理按鈕、F5重新整理、網頁右鍵“重新載入”:強快取失效,直接判斷協商快取
  • ctrl+F5強制重新整理:所有快取失效,重新請求伺服器資料

相關文章