圖解 HTTP 緩存

這是第 49 篇不摻水的原創,想獲取更多原創好文,請掃 👆上方二維碼關注咱們吧~
本文首發於前端早早聊公衆號:圖解 HTTP 緩存前端

前言

HTTP 的緩存機制,能夠說這是前端工程師須要掌握的重要知識點之一。本文將針對 HTTP 緩存總體的流程作一個詳細的講解,爭取作到你們讀完整篇文章後,對緩存有一個總體的瞭解。express

HTTP 緩存分爲 2 種,一種是強緩存,另外一種是協商緩存。主要做用是能夠加快資源獲取速度,提高用戶體驗,減小網絡傳輸,緩解服務端的壓力。這是緩存運做的一個總體流程圖:瀏覽器

Http緩存.jpg

強緩存

不須要發送請求到服務端,直接讀取瀏覽器本地緩存,在 Chrome 的 Network 中顯示的 HTTP 狀態碼是 200 ,在 Chrome 中,強緩存又分爲 Disk Cache (存放在硬盤中)和 Memory Cache (存放在內存中),存放的位置是由瀏覽器控制的。是否強緩存由 Expires、Cache-Control 和 Pragma 3 個 Header 屬性共同來控制。緩存

○ Expires

Expires 的值是一個 HTTP 日期,在瀏覽器發起請求時,會根據系統時間和 Expires 的值進行比較,若是系統時間超過了 Expires 的值,緩存失效。因爲和系統時間進行比較,因此當系統時間和服務器時間不一致的時候,會有緩存有效期不許的問題。Expires 的優先級在三個 Header 屬性中是最低的。服務器

○ Cache-Control

Cache-Control 是 HTTP/1.1 中新增的屬性,在請求頭和響應頭中均可以使用,經常使用的屬性值若有:網絡

  • max-age:單位是秒,緩存時間計算的方式是距離發起的時間的秒數,超過間隔的秒數緩存失效
  • no-cache:不使用強緩存,須要與服務器驗證緩存是否新鮮
  • no-store:禁止使用緩存(包括協商緩存),每次都向服務器請求最新的資源
  • private:專用於我的的緩存,中間代理、CDN 等不能緩存此響應
  • public:響應能夠被中間代理、CDN 等緩存
  • must-revalidate:在緩存過時前可使用,過時後必須向服務器驗證

○ Pragma

Pragma 只有一個屬性值,就是 no-cache ,效果和 Cache-Control 中的 no-cache 一致,不使用強緩存,須要與服務器驗證緩存是否新鮮,在 3 個頭部屬性中的優先級最高。前端工程師

本地經過 express 起一個服務來驗證強緩存的 3 個屬性,代碼以下:app

const express = require('express');
const app = express();
var options = { 
  etag: false, // 禁用協商緩存
  lastModified: false, // 禁用協商緩存
  setHeaders: (res, path, stat) => {
    res.set('Cache-Control', 'max-age=10'); // 強緩存超時時間爲10秒
  },
};
app.use(express.static((__dirname + '/public'), options));
app.listen(3000);
複製代碼

第一次加載,頁面會向服務器請求數據,並在 Response Header 中添加 Cache-Control ,過時時間爲 10 秒。post

緩存1.jpg

第二次加載,Date 頭屬性未更新,能夠看到瀏覽器直接使用了強緩存,實際沒有發送請求。性能

緩存2.jpg

過了 10 秒的超時時間以後,再次請求資源:

緩存3.jpg

當 Pragma 和 Cache-Control 同時存在的時候,Pragma 的優先級高於 Cache-Control。

緩存5.jpg

協商緩存

當瀏覽器的強緩存失效的時候或者請求頭中設置了不走強緩存,而且在請求頭中設置了If-Modified-Since 或者 If-None-Match 的時候,會將這兩個屬性值到服務端去驗證是否命中協商緩存,若是命中了協商緩存,會返回 304 狀態,加載瀏覽器緩存,而且響應頭會設置 Last-Modified 或者 ETag 屬性。

○ ETag/If-None-Match

ETag/If-None-Match 的值是一串 hash 碼,表明的是一個資源的標識符,當服務端的文件變化的時候,它的 hash碼會隨之改變,經過請求頭中的 If-None-Match 和當前文件的 hash 值進行比較,若是相等則表示命中協商緩存。ETag 又有強弱校驗之分,若是 hash 碼是以 "W/" 開頭的一串字符串,說明此時協商緩存的校驗是弱校驗的,只有服務器上的文件差別(根據 ETag 計算方式來決定)達到可以觸發 hash 值後綴變化的時候,纔會真正地請求資源,不然返回 304 並加載瀏覽器緩存。

○ Last-Modified/If-Modified-Since

Last-Modified/If-Modified-Since 的值表明的是文件的最後修改時間,第一次請求服務端會把資源的最後修改時間放到 Last-Modified 響應頭中,第二次發起請求的時候,請求頭會帶上上一次響應頭中的 Last-Modified 的時間,並放到 If-Modified-Since 請求頭屬性中,服務端根據文件最後一次修改時間和 If-Modified-Since 的值進行比較,若是相等,返回 304 ,並加載瀏覽器緩存。

本地經過 express 起一個服務來驗證協商緩存,代碼以下:

const express = require('express');
const app = express();
var options = { 
  etag: true, // 開啓協商緩存
  lastModified: true, // 開啓協商緩存
  setHeaders: (res, path, stat) => {
    res.set({
      'Cache-Control': 'max-age=00', // 瀏覽器不走強緩存
      'Pragma': 'no-cache', // 瀏覽器不走強緩存
    });
  },
};
app.use(express.static((__dirname + '/public'), options));
app.listen(3001);
複製代碼

第一次請求資源:

緩存6.jpg

第二次請求資源,服務端根據請求頭中的 If-Modified-Since 和 If-None-Match 驗證文件是否修改。

緩存7.jpg

咱們再來驗證一下 ETag 在強校驗的狀況下,只增長一行空格,hash 值如何變化,在代碼中,我採用的是對文件進行 MD5 加密來計算其 hash 值。

注:只是爲了演示用,實際計算不是經過 MD5 加密的,Apache 默認經過 FileEtag 中 FileEtag INode Mtime Size 的配置自動生成 ETag,用戶能夠經過自定義的方式來修改文件生成 ETag 的方式。

爲了保證 lastModified 不影響緩存,我把經過 Last-Modified/If-Modified-Since 請求頭刪除了,源碼以下:

const express = require('express');
const CryptoJS = require('crypto-js/crypto-js');
const fs = require('fs');
const app = express();
var options = { 
  etag: true, // 只經過Etag來判斷
  lastModified: false, // 關閉另外一種協商緩存
  setHeaders: (res, path, stat) => {
    const data = fs.readFileSync(path, 'utf-8'); // 讀取文件
    const hash = CryptoJS.MD5((JSON.stringify(data))); // MD5加密
    res.set({
      'Cache-Control': 'max-age=00', // 瀏覽器不走強緩存
      'Pragma': 'no-cache', // 瀏覽器不走強緩存
      'ETag': hash, // 手動設置Etag值爲MD5加密後的hash值
    });
  },
};
app.use(express.static((__dirname + '/public'), options));
app.listen(4000); // 使用新端口號,不然上面驗證的協商緩存會一直存在
複製代碼

第一次和第二次請求以下:

緩存10.jpg

緩存11.jpg

而後我修改了 test.js ,增長一個空格後再刪除一個空格,保持文件內容不變,但文件的修改時間改變,發起第三次請求,因爲我生成 ETag 的方式是經過對文件內容進行 MD5 加密生成,因此雖然修改時間變化了,但請求依然返回了 304 ,讀取瀏覽器緩存。

緩存13.jpg

ETag/If-None-Match 的出現主要解決了 Last-Modified/If-Modified-Since 所解決不了的問題:

  • 若是文件的修改頻率在秒級如下,Last-Modified/If-Modified-Since 會錯誤地返回 304
  • 若是文件被修改了,可是內容沒有任何變化的時候,Last-Modified/If-Modified-Since 會錯誤地返回 304 ,上面的例子就說明了這個問題

總結

在實際使用場景中,好比政採雲的官網。圖片、不常變化的 JS 等靜態資源都會使用緩存來提升頁面的加載速度。例如政採雲首頁的頂部導航欄,埋點 SDK 等等。

在文章的最後,咱們再次回到這張流程圖,這張圖涵蓋了 HTTP 緩存的總體流程,你們對總體流程熟悉後,也能夠本身動手經過 Node 來驗證下 HTTP 緩存。

Http緩存.jpg

推薦閱讀

多是最全的 「文本溢出截斷省略」 方案合集

圖文並茂,爲你揭開「單點登陸「的神祕面紗

招賢納士

政採雲前端團隊(ZooTeam),一個年輕富有激情和創造力的前端團隊,隸屬於政採雲產品研發部,Base 在風景如畫的杭州。團隊現有 50 餘個前端小夥伴,平均年齡 27 歲,近 3 成是全棧工程師,妥妥的青年風暴團。成員構成既有來自於阿里、網易的「老」兵,也有浙大、中科大、杭電等校的應屆新人。團隊在平常的業務對接以外,還在物料體系、工程平臺、搭建平臺、性能體驗、雲端應用、數據分析及可視化等方向進行技術探索和實戰,推進並落地了一系列的內部技術產品,持續探索前端技術體系的新邊界。

若是你想改變一直被事折騰,但願開始能折騰事;若是你想改變一直被告誡須要多些想法,卻無從破局;若是你想改變你有能力去作成那個結果,卻不須要你;若是你想改變你想作成的事須要一個團隊去支撐,但沒你帶人的位置;若是你想改變既定的節奏,將會是「 5 年工做時間 3 年工做經驗」;若是你想改變原本悟性不錯,但老是有那一層窗戶紙的模糊… 若是你相信相信的力量,相信平凡人能成就非凡事,相信能遇到更好的本身。若是你但願參與到隨着業務騰飛的過程,親手推進一個有着深刻的業務理解、完善的技術體系、技術創造價值、影響力外溢的前端團隊的成長曆程,我以爲咱們該聊聊。任什麼時候間,等着你寫點什麼,發給 ZooTeam@cai-inc.com

相關文章
相關標籤/搜索