阿里巴巴:瀏覽器的強緩存和協商緩存(一面)

面試公司:

阿里

面試環節:

一面

問題:

說一說你對瀏覽器強緩存和協商緩存的理解

阿里巴巴:瀏覽器的強緩存和協商緩存(一面)

答案:

這裡說的緩存是指瀏覽器(客戶端)在本地磁盤中對訪問過的資源保存的副本文件。

瀏覽器緩存主要有以下幾個優點:

  1. 減少重複數據請求,避免通過網絡再次加載資源,節省流量。
  2. 降低服務器的壓力,提升網站性能。
  3. 加快客戶端加載網頁的速度, 提升用戶體驗。

瀏覽器緩存分為強緩存和協商緩存,兩者有兩個比較明顯的區別:

  1. 如果瀏覽器命中強緩存,則不需要給服務器發請求;而協商緩存最終由服務器來決定是否使用緩存,即客戶端與服務器之間存在一次通信。
  2. 在 chrome 中強緩存(雖然沒有發出真實的 http 請求)的請求狀態碼返回是 200 (from cache);而協商緩存如果命中走緩存的話,請求的狀態碼是 304 (not modified)。 不同瀏覽器的策略不同,在 Fire Fox 中,from cache 狀態碼是 304.

其中 from cache 會分為 from disk cache 和 from memory cache. 從內存中獲取最快,但是是 session 級別的緩存,關閉瀏覽器之後就沒有了。

阿里巴巴:瀏覽器的強緩存和協商緩存(一面)

請求流程

瀏覽器在第一次請求後緩存資源,再次請求時,會進行下面兩個步驟:

  1. 瀏覽器會獲取該緩存資源的 header 中的信息,根據 response header 中的 expires 和 cache-control 來判斷是否命中強緩存,如果命中則直接從緩存中獲取資源。
  2. 如果沒有命中強緩存,瀏覽器就會發送請求到服務器,這次請求會帶上 IF-Modified-Since 或者 IF-None-Match, 它們的值分別是第一次請求返回 Last-Modified 或者 Etag,由服務器來對比這一對字段來判斷是否命中。如果命中,則服務器返回 304 狀態碼,並且不會返回資源內容,瀏覽器會直接從緩存獲取;否則服務器最終會返回資源的實際內容,並更新 header 中的相關緩存字段。

借用網上的一張圖片

阿里巴巴:瀏覽器的強緩存和協商緩存(一面)

強緩存

強緩存是根據返回頭中的 Expires 或者 Cache-Control 兩個字段來控制的,都是表示資源的緩存有效時間。

  • Expires 是 http 1.0 的規範,值是一個GMT 格式的時間點字符串,比如 Expires:Mon,18 Oct 2066 23:59:59 GMT 。這個時間點代表資源失效的時間,如果當前的時間戳在這個時間之前,則判定命中緩存。有一個缺點是,失效時間是一個絕對時間,如果服務器時間與客戶端時間偏差較大時,就會導致緩存混亂。而服務器的時間跟用戶的實際時間是不一樣是很正常的,所以 Expires 在實際使用中會帶來一些麻煩。
  • Cache-Control 這個字段是 http 1.1 的規範,一般常用該字段的 max-age 值來進行判斷,它是一個相對時間,比如 .Cache-Control:max-age=3600 代表資源的有效期是 3600 秒。並且返回頭中的 Date 表示消息發送的時間,表示當前資源在 Date ~ Date +3600s 這段時間裡都是有效的。不過我在實際使用中常常遇到設置了 max-age 之後,在 max-age 時間內重新訪問資源卻會返回 304 not modified ,這是由於服務器的時間與本地的時間不同造成的。當然 Cache-Control 還有其他幾個值可以設置, 不過相對來說都很少用了:no-cache 不使用本地緩存。需要使用協商緩存。no-store直接禁止瀏覽器緩存數據,每次請求資源都會向服務器要完整的資源, 類似於 network 中的 disabled cache。public 可以被所有用戶緩存,包括終端用戶和 cdn 等中間件代理服務器。private 只能被終端用戶的瀏覽器緩存。

如果 Cache-Control 與 Expires 同時存在的話, Cache-Control 的優先級高於 Expires 。

協商緩存

協商緩存是由服務器來確定緩存資源是否可用。 主要涉及到兩對屬性字段,都是成對出現的,即第一次請求的響應頭帶上某個字, Last-Modified 或者 Etag,則後續請求則會帶上對應的請求字段 If-Modified-Since或者 If-None-Match,若響應頭沒有 Last-Modified 或者 Etag 字段,則請求頭也不會有對應的字段。

  • Last-Modified/If-Modified-Since 二者的值都是GMT格式的時間字符串, Last-Modified 標記最後文件修改時間, 下一次請求時,請求頭中會帶上 If-Modified-Since 值就是 Last-Modified 告訴服務器我本地緩存的文件最後修改的時間,在服務器上根據文件的最後修改時間判斷資源是否有變化, 如果文件沒有變更則返回 304 Not Modified ,請求不會返回資源內容,瀏覽器直接使用本地緩存。當服務器返回 304 Not Modified 的響應時,response header 中不會再添加的 Last-Modified 去試圖更新本地緩存的 Last-Modified, 因為既然資源沒有變化,那麼 Last-Modified 也就不會改變;如果資源有變化,就正常返回返回資源內容,新的 Last-Modified 會在 response header 返回,並在下次請求之前更新本地緩存的 Last-Modified,下次請求時,If-Modified-Since會啟用更新後的 Last-Modified。
  • Etag/If-None-Match, 值都是由服務器為每一個資源生成的唯一標識串,只要資源有變化就這個值就會改變。服務器根據文件本身算出一個哈希值並通過 ETag字段返回給瀏覽器,接收到 If-None-Match 字段以後,服務器通過比較兩者是否一致來判定文件內容是否被改變。與 Last-Modified 不一樣的是,當服務器返回 304 Not Modified 的響應時,由於在服務器上ETag 重新計算過,response header中還會把這個 ETag 返回,即使這個 ETag 跟之前的沒有變化。

HTTP中並沒有指定如何生成 ETag,可以由開發者自行生成,哈希是比較理想的選擇。

為什麼要有 Etag

HTTP1.1 中 Etag 的出現主要是為了解決幾個 Last-Modified 比較難解決的問題:

  • 一些文件也許會週期性的更改,但是內容並不改變(僅僅改變的修改時間),這個時候我們並不希望客戶端認為這個文件被修改了,而重新GET;
  • 某些文件修改非常頻繁,比如在秒以下的時間內進行修改,(比方說1s內修改了N次),If-Modified-Since 能檢查到的粒度是秒級的,使用 Etag 就能夠保證這種需求下客戶端在1秒內能刷新 N 次 cache。
  • 某些服務器不能精確的得到文件的最後修改時間。

優先級

Cache-Control > expires > Etag > Last-Modified

用戶行為對緩存的影響

經過對qq、fire fox 、safari 、chrome 這幾個瀏覽器的訪問同一個頁面測試我發現,不同的瀏覽器在 F5 刷新的時候 ,同一個文件 qq 、fire fox 瀏覽器會返回 304 Not Nodified,在請求頭中不攜帶 Expires/Cache-Control; 而 chrome 和 safari 刷新的時候,會返回 200 from cache, 沒有真正發起請求,走強緩存。可見不同的瀏覽器反饋是不一致的,所以下面表格中"F5刷新"時 Expires/Cache-Control 會無效我認為是存在一定爭議的。

而 Ctrl + F5 強制刷新的時候,會暫時禁用強緩存和協商緩存。

阿里巴巴:瀏覽器的強緩存和協商緩存(一面)

如何設置強緩存和協商緩存

  1. 後端服務器,寫入代碼邏輯中:res.setHeader('max-age': '3600 public') res.setHeader(etag: '5c20abbd-e2e8') res.setHeader('last-modified': Mon, 24 Dec 2018 09:49:49 GMT)
  2. Nginx 配置add_header Cache-Control "max-age=3600" 一般來說,通過 nginx 靜態資源服務器,會默認給資源帶上強緩存、協商緩存的 header 字段。
阿里巴巴:瀏覽器的強緩存和協商緩存(一面)

兩個示例

  1. 如果在 cache-control 定義的 max-age 時間之內,js, css 文件會走強緩存,http 狀態碼是 200, 跟服務器也並不會有交互。但是第一個文件 index.html 文件, 每次回車或者刷新都是狀態碼都是 304 ,因為它的請求頭中默認每次都攜帶了 Cache-Control: max-age=0 。
阿里巴巴:瀏覽器的強緩存和協商緩存(一面)

阿里巴巴:瀏覽器的強緩存和協商緩存(一面)

  1. js css 文件 cache-control 超時之後,重新按回車會走協商緩存,請求服務器發現資源沒有改變,於是返回 304 ,瀏覽器從緩存中獲取內容,從 size 中也可以看出端倪, 幾百 B 的包不是靜態資源的體積。
阿里巴巴:瀏覽器的強緩存和協商緩存(一面)

三級緩存原理(大白話)

最後總結一下瀏覽器的三級緩存原理:

  1. 先去內存看,如果有,直接加載
  2. 如果內存沒有,擇取硬盤獲取,如果有直接加載
  3. 如果硬盤也沒有,那麼就進行網絡請求
  4. 加載到的資源緩存到硬盤和內存

原文:https://github.com/frontend9/fe9-interview/issues/29


分享到:


相關文章: