前端也要懂Http快取機制

2021-09-11 14:48:12 字數 4384 閱讀 3624

最近在看面試題的時候總會看到有一些關於http快取的題目,但是總是一知半解,不甚理解;尤其是http頭資訊中有一大堆的字段,什麼if-modified-since,什麼if-none-match,真是令人頭疼。後來突然想到,要是能通過自己構建乙個伺服器,自己新增頭資訊,然後看實現的效果,不就更好了麼。說幹就幹,在網上各種找資料,然後再使用expressjs新增各種頭資訊,就能夠很好的理解http快取了。

個人部落格了解下謝小飛的部落格

瀏覽器和伺服器之間通訊是通過http協議,http協議永遠都是客戶端發起請求,伺服器回送響應。模型如下:

http報文就是瀏覽器和伺服器間通訊時傳送及響應的資料塊。瀏覽器向伺服器請求資料,傳送請求(request)報文;伺服器向瀏覽器返回資料,返回響應(response)報文。報文資訊主要分為兩部分:

資料主體部分:http請求真正想要傳輸的資料內容

欄位名稱

字段所屬

pragma

通用頭expires

響應頭cache-control

通用頭last-modified

響應頭if-modified-sice

請求頭etag

響應頭if-none-match

請求頭

http快取可以分為兩大類,強制快取(也稱強快取)和協商快取。兩類快取規則不同,強制快取在快取資料未失效的情況下,不需要再和伺服器發生互動;而協商快取,顧名思義,需要進行比較判斷是否可以使用快取。

兩類快取規則可以同時存在,強制快取優先順序高於協商快取,也就是說,當執行強制快取的規則時,如果快取生效,直接使用快取,不再執行協商快取規則。

我們先簡單搭建乙個express的伺服器,不加任何快取資訊頭。

})複製**我們可以看到請求結果如下:

請求過程如下:

看得出來這種請求方式的流量與請求次數有關,同時,缺點也很明顯:

接下來我們開始在頭資訊中新增快取資訊。

強制快取分為兩種情況,expires和cache-control。

expires的值是伺服器告訴瀏覽器的快取過期時間(值為gmt時間,即格林尼治時間),即下一次請求時,如果瀏覽器端的當前時間還沒有到達過期時間,則直接使用快取資料。下面通過我們的express伺服器來設定一下expires響應頭資訊。

//其他**...

const moment = require('moment');

let jspath = path.resolve(__dirname,'./static/js/demo.js');

let cont = fs.readfilesync(jspath);

res.setheader('expires', getglnz()) //2分鐘

res.end(cont)

})function

getglnz()

//其他**...

複製**

我們在demo.js中新增了乙個expires響應頭,不過由於是格林尼治時間,所以通過momentjs轉換一下。第一次請求的時候還是會向伺服器發起請求,同時會把過期時間和檔案一起返回給我們;但是當我們重新整理的時候,才是見證奇蹟的時刻:

可以看出檔案是直接從快取(memory cache)中讀取的,並沒有發起請求。我們在這邊設定過期時間為兩分鐘,兩分鐘過後可以重新整理一下頁面看到瀏覽器再次傳送請求了。

雖然這種方式新增了快取控制,節省流量,但是還是有以下幾個問題的:

不過expires 是http 1.0的東西,現在預設瀏覽器均預設使用http 1.1,所以它的作用基本忽略。

針對瀏覽器和伺服器時間不同步,加入了新的快取方案;這次伺服器不是直接告訴瀏覽器過期時間,而是告訴乙個相對時間cache-control=10秒,意思是10秒內,直接使用瀏覽器快取。

let jspath = path.resolve(__dirname,'./static/js/demo.js');

let cont = fs.readfilesync(jspath);

res.setheader('cache-control', 'public,max-age=120') //2分鐘

res.end(cont)

})複製**

強制快取的弊端很明顯,即每次都是根據時間來判斷快取是否過期;但是當到達過期時間後,如果檔案沒有改動,再次去獲取檔案就有點浪費伺服器的資源了。協商快取有兩組報文結合使用:

last-modified和if-modified-since

etag和if-none-match

為了節省伺服器的資源,再次改進方案。瀏覽器和伺服器協商,伺服器每次返回檔案的同時,告訴瀏覽器檔案在伺服器上最近的修改時間。請求過程如下:

**實現過程如下:

let jspath = path.resolve(__dirname,'./static/js/demo.js')

let cont = fs.readfilesync(jspath);

let status = fs.statsync(jspath)

let lastmodified = status.mtime.toutcstring()

if(lastmodified === req.headers['if-modified-since']) else

})複製**

我們多次重新整理頁面,可以看到請求結果如下:

雖然這個方案比前面三個方案有了進一步的優化,瀏覽器檢測檔案是否有修改,如果沒有變化就不再傳送檔案;但是還是有以下缺點:

為了解決檔案修改時間不精確帶來的問題,伺服器和瀏覽器再次協商,這次不返回時間,返回檔案的唯一標識etag。只有當檔案內容改變時,etag才改變。請求過程如下:

const md5 = require('md5');

let jspath = path.resolve(__dirname,'./static/js/demo.js');

let cont = fs.readfilesync(jspath);

let etag = md5(cont);

if(req.headers['if-none-match'] === etag) else

})複製**

請求結果如下:

在報文頭的**中我們可以看到有乙個欄位叫pragma,這是一段塵封的歷史....

在「遙遠的」http1.0時代,給客戶端設定快取方式可通過兩個欄位--pragma和expires。雖然這兩個欄位早可拋棄,但為了做http協議的向下相容,你還是可以看到很多**依舊會帶上這兩個字段。

當該字段值為no-cache的時候,會告訴瀏覽器不要對該資源快取,即每次都得向伺服器發一次請求才行。

res.setheader('pragma', 'no-cache') //禁止快取

res.setheader('cache-control', 'public,max-age=120') //2分鐘

複製**

通過pragma來禁止快取,通過cache-control設定兩分鐘快取,但是重新訪問我們會發現瀏覽器會再次發起一次請求,說明了pragma的優先順序高於cache-control

我們看到cache-control中有乙個屬性是public,那麼這代表了什麼意思呢?其實cache-control不光有max-age,它常見的取值private、public、no-cache、max-age,no-store,預設值為private,各個取值的含義如下:

所以我們在重新整理頁面的時候,如果只按f5只是單純的傳送請求,按ctrl+f5會發現請求頭上多了兩個欄位pragma: no-cache和cache-control: no-cache。

上面我們說過強制快取的優先順序高於協商快取,pragma的優先順序高於cache-control,那麼其他快取的優先順序順序怎麼樣呢?網上查閱了資料得出以下順序(ps:有興趣的童鞋可以驗證一下正確性告訴我):

pragma > cache-control > expires > etag > last-modified

Http快取機制

快取快取,就是把需要的東西存起來,不需要每次都去請求。主要目的減小伺服器壓力,放到客戶端上來講,還利於節省流量,還能流暢的把ui顯示出來,提高了使用者體驗。對於http快取來講,主要的就是校驗快取的有效性,也就是新鮮度。如果客戶端不能及時響應服務端的資料變化,快取一直不能被更新,那不就是得不償失了?...

HTTP 快取機制

基於 header的示例 content length 3534http快取策略分為 1 快取策略 cache control 頭里的 public private no cache max age no store 其中no store為不儲存,no cache 0秒的max age 2 快取過期...

http快取機制

首先需要了解http協議的響應頭中的幾個欄位的含義 cache control expires 該欄位表示資源的過期時間。etag 該欄位表示資源的唯一標識。last modified 該欄位表示資源的最後修改時間。有以下2個問題需要注意 為什麼優先校驗etag,後校驗last modified?因...