前端面試查漏補缺--(六) 瀏覽器快取

shotCat發表於2019-02-22

前言

本系列最開始是為了自己面試準備的.後來發現整理越來越多,差不多有十二萬字元,最後決定還是分享出來給大家.

為了分享整理出來,花費了自己大量的時間,起碼是隻自己用的三倍時間.如果喜歡的話,歡迎收藏,關注我!謝謝!

文章連結

合集篇:

前端面試查漏補缺--Index篇(12萬字元合集) 包含目前已寫好的系列其他十幾篇文章.後續新增值文章不會再在每篇新增連結,強烈建議議點贊,關注合集篇!!!!,謝謝!~

後續更新計劃

後續還會繼續新增設計模式,前端工程化,專案流程,部署,閉環,vue常考知識點 等內容.如果覺得內容不錯的話歡迎收藏,關注我!謝謝!

求一份內推

目前本人也在準備跳槽,希望各位大佬和HR小姐姐可以內推一份靠譜的武漢 前端崗位!郵箱:bupabuku@foxmail.com.謝謝啦!~

瀏覽器快取概述

什麼是快取

快取(和我之前文章所說的前端儲存是不一樣的!注意區分):
是一種儲存資源副本並在下次請求時直接使用該副本的技術。那麼瀏覽器快取就是瀏覽器請求網站留下的資源副本。

當 web 快取發現請求的資源已經被儲存,它會攔截請求,返回該資源的拷貝,而不會去源伺服器重新下載。

快取的好處

  • 緩解伺服器壓力(不用每次去請求資源);
  • 提升效能(開啟本地資源速度當然比請求回來再開啟要快得多);
  • 減少頻寬消耗;

快取的分類

快取在巨集觀上可以分成兩類:

  • 私有快取: 只能用於單獨使用者,最常見的就是瀏覽器快取,也是本片重點講解的.
  • 共享快取: 夠被多個使用者使用的快取,也就是那些能被各級代理的快取.

瀏覽器的快取策略

瀏覽器對於快取的處理是根據第一次請求資源時返回的響應頭來確定的。

根據響應頭,瀏覽器快取策略一般分為三種:強快取,協商快取啟發式快取

瀏覽器常見欄位和指令

在講強快取和協商快取之前先提前瞭解以下這幾個欄位和指令,便於後面理解:

  1. expires: 告知客戶端資源快取失效的絕對時間
  2. last-modified: 資源最後一次修改的時間
  3. Etag: 檔案的特殊標識
  4. cache-control:告訴客戶端或是伺服器如何處理快取。
  5. private: cache-control裡的響應指令.表示客戶端可以快取
  6. public: cache-control裡的響應指令.表示客戶端和代理伺服器都可快取.如果沒有明確指定private,則預設為public。
  7. no-cache: cache-control裡的指令.表示需要可以快取,但每次用應該去向伺服器驗證快取是否可用
  8. no-store: cache-control欄位裡的指令.表示所有內容都不會快取,強制快取,對比快取都不會觸發.
  9. max-age=xxx: cache-control欄位裡的指令.表示快取的內容將在 xxx 秒後失效

強快取

強快取簡單理解就是:給瀏覽器快取設定過期時間,超過這個時間之後快取就是過期,瀏覽器需要重新請求。

強快取主要是通過http請求頭中的Cache-Control和Expires兩個欄位控制

expires

expires是一個HTTP/1.0的欄位,它給瀏覽器設定了一個絕對時間,當瀏覽器時間超過這個絕對時間之後,重新向伺服器傳送請求。

用法:

它描述的是一個絕對時間,用GMT格式的字串表示

Expires: Wed Feb 20 2019 11:25:41 GMT
複製程式碼

也可以在html檔案裡直接使用:

<meta http-equiv="expires" content="Wed Feb 20 2019 11:25:41 GMT">
複製程式碼

弊端:

  • Expires返回的是伺服器的時間,但判斷的時候用的卻是客戶端的時間,這就導致Expires很被動,因為使用者有可能改變客戶端的時間,導致快取時間判斷出錯,這也是引入Cache-Control:max-age指令的原因之一。

cache-control: max-age

為了解決expires存在的問題,Http1.1版本中提出了cache-control:max-age,該欄位與expires的快取思路相同,都是設定了一個過期時間,不同的是max-age設定的是相對快取時間開始往後的多少秒,因此不再受日期不準確情況的影響。

優先順序:
在優先順序上:max-age>Expires。當兩者同時出現在響應頭時,Expires將被max-age覆蓋.

用法:

Cache-control: max-age=666
複製程式碼

表示資源會在 666 秒後過期,需要再次請求。

強快取在瀏覽器上的表現

  • Firefox瀏覽器對強快取表現為一個灰色的200狀態碼。
  • Chrome瀏覽器狀態碼錶現為:200 (from disk cache)或是200 OK (from memory cache)

說明:Chrome會根據本地記憶體的使用率來決定快取存放在哪,如果記憶體使用率很高,放在磁碟裡面,磁碟的使用率很高會暫時放在記憶體裡面。這就可以比較合理的解釋了為什麼同一個資源有時是from memory cache有時是from disk cache的問題了。

但是強制快取存在一個問題,該快取方式優先順序高,如果在過期時間內快取的資源在伺服器上更新了,客服端不能及時獲取最新的資源。這時怎麼辦?於是就有了協商快取.

協商快取

協商快取解決了無法及時獲取更新資源的問題。它利用下面會講到的兩組欄位,對資源做標識.然後由伺服器做分析,如果資源未更新,則返回304狀態碼.那麼瀏覽器則會從快取中讀取資源,否則重新請求資源。

協商快取是利用的是【Last-Modified,If-Modified-Since】和【ETag、If-None-Match】這兩對Header來管理的。

Last-Modified與If-Modified-Since

  • 1,瀏覽器第一次向伺服器請求資源,伺服器會在返回這個資源的同時,在response的header加上Last-Modified的header,這個header表示這個資源在伺服器上的最後修改時間:Last-Modified: Wed Feb 20 2019 14:08:32 GMT

  • 2,瀏覽器之後再向伺服器請求這個資源時,在request的header上加上If-Modified-Since的header,這個header的值就是上一次請求時返回的Last-Modified的值:Last-Modified: Wed Feb 20 2019 14:08:32 GMT

  • 3,伺服器再次收到資源請求時,根據瀏覽器傳過來If-Modified-Since和資源在伺服器上的最後修改時間判斷資源是否有變化,如果沒有變化則返回304 Not Modified,但是不會返回資源內容;如果有變化,返回200,就正常返回資源內容。

    • 當伺服器返回304 Not Modified的響應時,response的header中不會再新增Last-Modified的header,因為既然資源沒有變化,那麼Last-Modified也就不會改變,這是伺服器返回304時的response header.
  • 4,瀏覽器收到304的響應後,就會從快取中載入資源。

  • 5,瀏覽器收到200的響應後,則從伺服器載入新資源時,Last-Modified Header在重新載入的時候會被更新,下次請求時,If-Modified-Since會啟用上次返回的Last-Modified值。

弊端:
【Last-Modified,If-Modified-Since】都是根據伺服器時間返回的header,一般來說,在沒有調整伺服器時間和篡改客戶端快取的情況下,這兩個header配合起來管理協商快取是非常可靠的,但是它們是以秒為單位進行更新,如果小於該單位高頻進行更新的話,則不適合採用該方法。 這時候協商緩就不那麼的可靠了。所以就有了另外一對header來管理協商快取,這對header就是【ETag、If-None-Match】。

ETag與If-None-Match

  • 1,瀏覽器第一次跟伺服器請求一個資源,伺服器在返回這個資源的同時,在response的header加上ETag的header,這個header是伺服器根據當前請求的資源生成的一個唯一標識,這個唯一標識是一個字串ETag: shotcat-66666只要資源有變化這個串就不同,跟最後修改時間沒有關係,所以能很好的補充Last-Modified的問題.
  • 2,瀏覽器再次跟伺服器請求這個資源時,在request的header上加上If-None-Match的header,這個header的值就是上一次請求時返回的ETag的值If-None-Match: shotcat-66666.
  • 3,伺服器再次收到資源請求時,根據瀏覽器傳過來If-None-Match和然後再根據資源生成一個新的ETag,如果這兩個值相同就說明資源沒有變化,否則就是有變化;如果沒有變化則返回304 Not Modified,但是不會返回資源內容;如果有變化,則返回200,並正常返回資源內容。與Last-Modified不一樣的是,當伺服器返回304 Not Modified的響應時,由於ETag重新生成過,response header中還會把這個ETag返回,即使這個ETag跟之前的沒有變化
  • 4,瀏覽器收到304的響應後,就會從快取中載入資源。
  • 5,瀏覽器收到200的響應後,則從伺服器載入新資源時,ETag在重新載入的時候會被更新,下次請求時,If-None-Match會啟用上次返回的ETag值。

Etag和Last-Modified非常相似,都是用來判斷一個引數,從而決定是否啟用快取。但是ETag相對於Last-Modified也有其優勢,可以更加準確的判斷檔案內容是否被修改, 從而在實際操作中實用程度也更高,但缺點也很明顯,由於需要對資源進行生成標識,效能方面就勢必有所犧牲

優先順序:
ETag與If-None-Match > Last-Modified與If-Modified-Since, 同時存在時, 前者覆蓋後者.

啟發式快取

我跟我們的請求頭中確定快取過期時間的欄位一個都沒有.例如:

Age:23146
Cache-Control: public
Date:Tue, 28 Nov 2017 12:26:41 GMT
Last-Modified:Tue, 28 Nov 2017 05:14:02 GMT
Vary:Accept-Encoding
複製程式碼

此時則會預設觸發瀏覽器啟發式快取:
瀏覽器會根據響應頭中2個時間欄位 Date 和 Last-Modified 之間的時間差值,取其值的10%作為快取時間週期。

快取的優先順序

在快取策略上:強快取>協商快取>啟發式快取

進一步分析可得出,以下優先順序:
Cache-Control > Expires > ETag > Last-Modified

非常注意

其他所有教程都是告訴你瀏覽器肯定是先檢查強快取,再檢查協商快取!但實際它們都忽略了一點.其實瀏覽器是先檢查Cache-Control,如果為no-store.則瀏覽器 所有內容都不會快取,強制快取,協商快取統統都不會觸發!!!

補充:Pragma

它是HTTP/1.0裡面的一個欄位,在http1.1已被拋棄,使用Cache-Control替代.但為了做http協議的向下相容,很多網站依舊會帶上這個欄位但優先順序很高.

測試發現,Chrome和Firefox中Pragma的優先順序高於Cache-Control和Expires.

一般可能會這麼用:<meta http-equiv="Pragma" content="no-cache">

服務端響應新增'Pragma': 'no-cache',瀏覽器表現行為和強制重新整理類似。

補充: Cache-Control

通過cache-control的指令可以控制告訴客戶端或是伺服器如何處理快取。這也是11個欄位中指令最多的一個,也是經常被用到的.

請求指令

指令 引數 說明
no-cache 強制源伺服器再次驗證
no-store 不快取請求或是響應的任何內容
max-age=[秒] 快取時長,單位是秒 快取的時長,也是響應的最大的Age值
min-fresh=[秒] 必需 期望在指定時間內響應仍然有效
no-transform 代理不可更改媒體型別
only-if-cached 從快取獲取
cache-extension - 新的指令標記(token)

響應指令

指令 引數 說明
public 任意一方都能快取該資源(客戶端、代理伺服器等)
private 可省略 只能特定使用者快取該資源
no-cache 可省略 快取前必須先確認其有效性
no-store 不快取請求或響應的任何內容
no-transform 代理不可更改媒體型別
must-revalidate 可快取但必須再向源伺服器進確認
proxy-revalidate 要求中間快取伺服器對快取的響應有效性再進行確認
max-age=[秒] 快取時長,單位是秒 快取的時長,也是響應的最大的Age值
s-maxage=[秒] 必需 公共快取伺服器響應的最大Age值
cache-extension - 新指令標記(token

請注意no-cache指令很多人誤以為是不快取,這是不準確的,no-cache的意思是可以快取,但每次用應該去想伺服器驗證快取是否可用。no-store才是不快取內容。 另外部分指令也可以組合使用,比如:

Cache-Control: max-age=100, must-revalidate, public
複製程式碼
複製程式碼

上面指令的意思是快取的有效時間為100秒,之後訪問需要向源伺服器傳送請求驗證,此快取可被代理伺服器和客戶端快取。

瀏覽器快取判斷流程

前端面試查漏補缺--(六) 瀏覽器快取

HTTP中和快取相關的首部欄位

HTTP報文,主要由以下兩部分構成:

  1. 首部(header):包含了很多欄位,比如:cookie、快取、報文大小、報文格式等等);
  2. 主體(body):HTTP請求真正要傳輸的部分,比如:一個HTML文件,一個js檔案;

以上我們知道瀏覽器對於快取的處理過程,也簡單的提到了幾個相關的欄位。?接下來我們具體看下這幾個欄位:

1. 通用首部欄位

欄位名稱 說明
Cache-Control 控制快取具體的行為
Pragma HTTP1.0時的遺留欄位,當值為"no-cache"時強制驗證快取
Date 建立報文的日期時間(啟發式快取階段會用到這個欄位)

2. 響應首部欄位

欄位名稱 說明
ETag 伺服器生成資源的唯一標識
Vary 代理伺服器快取的管理資訊
Age 資源在快取代理中存貯的時長(取決於max-age和s-maxage的大小)

3. 請求首部欄位

欄位名稱 說明
If-Match 條件請求,攜帶上一次請求中資源的ETag,伺服器根據這個欄位判斷檔案是否有新的修改
If-None-Match 和If-Match作用相反,伺服器根據這個欄位判斷檔案是否有新的修改
If-Modified-Since 比較資源前後兩次訪問最後的修改時間是否一致
If-Unmodified-Since 比較資源前後兩次訪問最後的修改時間是否一致

4. 實體首部欄位

欄位名稱 說明
Expires 告知客戶端資源快取失效的絕對時間
Last-Modified 資源最後一次修改的時間

使用者操作行為對快取的影響

操作 說明
開啟新視窗 如果指定cache-control的值為private、no-cache、must-revalidate,那麼開啟新視窗訪問時都會重新訪問伺服器。而如果指定了max-age值,那麼在此值內的時間裡就不會重新訪問伺服器,例如:Cache-control: max-age=5 表示當訪問此網頁後的5秒內不會去再次訪問伺服器.
在位址列回車 如果值為private或must-revalidate,則只有第一次訪問時會訪問伺服器,以後就不再訪問。如果值為no-cache,那麼每次都會訪問。如果值為max-age,則在過期之前不會重複訪問。
按後退按扭 如果值為private、must-revalidate、max-age,則不會重訪問,而如果為no-cache,則每次都重複訪問.
按重新整理按扭 無論為何值,都會重複訪問.(可能返回狀態碼:200、304,這個不同瀏覽器處理是不一樣的,FireFox正常,Chrome則會啟用快取(200 from cache))
按強制重新整理按鈕 當做首次進入重新請求(返回狀態碼200)

如果想在瀏覽器點選“重新整理”按鈕的時候不讓瀏覽器去發新的驗證請求呢?辦法找到一個,知乎上面一個回答,在頁面載入完畢後通過指令碼動態地新增資源:

$(window).load(function() {
  	var bg='http://img.infinitynewtab.com/wallpaper/100.jpg';
  	setTimeout(function() {
    	$('#bgOut').css('background-image', 'url('+bg+')');
  	},0);
});
複製程式碼

選擇合適的快取策略

對於大部分的場景都可以使用強快取配合協商快取解決,但是在一些特殊的地方可能需要選擇特殊的快取策略

  • 對於某些不需要快取的資源,可以使用 Cache-control: no-store ,表示該資源不需要快取
  • 對於頻繁變動的資源,可以使用 Cache-Control: no-cache 並配合 ETag 使用,表示該資源已被快取,但是每次都會傳送請求詢問資源是否更新。
  • 對於程式碼檔案來說,通常使用 Cache-Control: max-age=31536000 並配合策略快取使用,然後對檔案進行指紋處理,一旦檔名變動就會立刻下載新的檔案。

感謝及參考

相關文章