日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > HTML >内容正文

HTML

前端也要懂Http缓存机制

發布時間:2023/12/15 HTML 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 前端也要懂Http缓存机制 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

??最近在看面試題的時候總會看到有一些關于Http緩存的題目,但是總是一知半解,不甚理解;尤其是Http頭信息中有一大堆的字段,什么if-modified-since,什么if-none-match,真是令人頭疼。后來突然想到,要是能通過自己構建一個服務器,自己添加頭信息,然后看實現的效果,不就更好了么。說干就干,在網上各種找資料,然后再使用expressjs添加各種頭信息,就能夠很好的理解Http緩存了。

個人博客了解下謝小飛的博客

Http簡介

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

??HTTP報文就是瀏覽器和服務器間通信時發送及響應的數據塊。瀏覽器向服務器請求數據,發送請求(request)報文;服務器向瀏覽器返回數據,返回響應(response)報文。報文信息主要分為兩部分:

  • 報文頭部:一些附加信息(cookie,緩存信息等),與緩存相關的規則信息,均包含在頭部中
  • 數據主體部分:HTTP請求真正想要傳輸的數據內容
  • ??本文用到的一些報文頭如下:

    字段名稱字段所屬
    Pragma通用頭
    Expires響應頭
    Cache-Control通用頭
    Last-Modified響應頭
    If-Modified-Sice請求頭
    ETag響應頭
    If-None-Match請求頭

    Http緩存的分類

    ??Http緩存可以分為兩大類,強制緩存(也稱強緩存)和協商緩存。兩類緩存規則不同,強制緩存在緩存數據未失效的情況下,不需要再和服務器發生交互;而協商緩存,顧名思義,需要進行比較判斷是否可以使用緩存。

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

    原始模型

    ??我們先簡單搭建一個Express的服務器,不加任何緩存信息頭。

    const express = require('express'); const app = express(); const port = 8080; const fs = require('fs'); const path = require('path');app.get('/',(req,res) => {res.send(`<!DOCTYPE html><html lang="en"><head><title>Document</title></head><body>Http Cache Demo<script src="/demo.js"></script></body></html>`) })app.get('/demo.js',(req, res)=>{let jsPath = path.resolve(__dirname,'./static/js/demo.js');let cont = fs.readFileSync(jsPath);res.end(cont) })app.listen(port,()=>{console.log(`listen on ${port}`) }) 復制代碼

    ??我們可以看到請求結果如下:

    ??請求過程如下:

    • 瀏覽器請求靜態資源demo.js
    • 服務器讀取磁盤文件demo.js,返給瀏覽器
    • 瀏覽器再次請求,服務器又重新讀取磁盤文件 a.js,返給瀏覽器。
    • 循環請求。。

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

    • 浪費用戶流量
    • 浪費服務器資源,服務器要讀磁盤文件,然后發送文件到瀏覽器
    • 瀏覽器要等待js下載并且執行后才能渲染頁面,影響用戶體驗

    ??接下來我們開始在頭信息中添加緩存信息。

    一、強制緩存

    ??強制緩存分為兩種情況,Expires和Cache-Control。

    Expires

    ??Expires的值是服務器告訴瀏覽器的緩存過期時間(值為GMT時間,即格林尼治時間),即下一次請求時,如果瀏覽器端的當前時間還沒有到達過期時間,則直接使用緩存數據。下面通過我們的Express服務器來設置一下Expires響應頭信息。

    //其他代碼... const moment = require('moment');app.get('/demo.js',(req, res)=>{let jsPath = path.resolve(__dirname,'./static/js/demo.js');let cont = fs.readFileSync(jsPath);res.setHeader('Expires', getGLNZ()) //2分鐘res.end(cont) })function getGLNZ(){return moment().utc().add(2,'m').format('ddd, DD MMM YYYY HH:mm:ss')+' GMT'; } //其他代碼... 復制代碼

    ??我們在demo.js中添加了一個Expires響應頭,不過由于是格林尼治時間,所以通過momentjs轉換一下。第一次請求的時候還是會向服務器發起請求,同時會把過期時間和文件一起返回給我們;但是當我們刷新的時候,才是見證奇跡的時刻:

    ??可以看出文件是直接從緩存(memory cache)中讀取的,并沒有發起請求。我們在這邊設置過期時間為兩分鐘,兩分鐘過后可以刷新一下頁面看到瀏覽器再次發送請求了。

    ??雖然這種方式添加了緩存控制,節省流量,但是還是有以下幾個問題的:

    • 由于瀏覽器時間和服務器時間不同步,如果瀏覽器設置了一個很后的時間,過期時間一直沒有用
    • 緩存過期后,不管文件有沒有發生變化,服務器都會再次讀取文件返回給瀏覽器

    ??不過Expires 是HTTP 1.0的東西,現在默認瀏覽器均默認使用HTTP 1.1,所以它的作用基本忽略。

    Cache-Control

    ??針對瀏覽器和服務器時間不同步,加入了新的緩存方案;這次服務器不是直接告訴瀏覽器過期時間,而是告訴一個相對時間Cache-Control=10秒,意思是10秒內,直接使用瀏覽器緩存。

    app.get('/demo.js',(req, res)=>{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
  • Last-Modified

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

    • 瀏覽器請求靜態資源demo.js
    • 服務器讀取磁盤文件demo.js,返給瀏覽器,同時帶上文件上次修改時間 Last-Modified(GMT標準格式)
    • 當瀏覽器上的緩存文件過期時,瀏覽器帶上請求頭If-Modified-Since(等于上一次請求的Last-Modified)請求服務器
    • 服務器比較請求頭里的If-Modified-Since和文件的上次修改時間。如果果一致就繼續使用本地緩存(304),如果不一致就再次返回文件內容和Last-Modified。
    • 循環請求。。

    ??代碼實現過程如下:

    app.get('/demo.js',(req, res)=>{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']){res.writeHead(304, 'Not Modified')res.end()} else {res.setHeader('Cache-Control', 'public,max-age=5')res.setHeader('Last-Modified', lastModified)res.writeHead(200, 'OK')res.end(cont)} }) 復制代碼

    ??我們多次刷新頁面,可以看到請求結果如下:

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

    • 由于Last-Modified修改時間是GMT時間,只能精確到秒,如果文件在1秒內有多次改動,服務器并不知道文件有改動,瀏覽器拿不到最新的文件
    • 如果服務器上文件被多次修改了但是內容卻沒有發生改變,服務器需要再次重新返回文件。

    ETag

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

    • 瀏覽器請求靜態資源demo.js
    • 服務器讀取磁盤文件demo.js,返給瀏覽器,同時帶上文件的唯一標識ETag
    • 當瀏覽器上的緩存文件過期時,瀏覽器帶上請求頭If-None-Match(等于上一次請求的ETag)請求服務器
    • 服務器比較請求頭里的If-None-Match和文件的ETag。如果一致就繼續使用本地緩存(304),如果不一致就再次返回文件內容和ETag。
    • 循環請求。。
    const md5 = require('md5');app.get('/demo.js',(req, res)=>{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){res.writeHead(304, 'Not Modified');res.end();} else {res.setHeader('ETag', etag);res.writeHead(200, 'OK');res.end(cont);} }) 復制代碼

    ??請求結果如下:

    一些額外的東西

    ??在報文頭的表格中我們可以看到有一個字段叫Pragma,這是一段塵封的歷史....

    ??在“遙遠的”http1.0時代,給客戶端設定緩存方式可通過兩個字段--Pragma和Expires。雖然這兩個字段早可拋棄,但為了做http協議的向下兼容,你還是可以看到很多網站依舊會帶上這兩個字段。

    關于Pragma

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

    res.setHeader('Pragma', 'no-cache') //禁止緩存 res.setHeader('Cache-Control', 'public,max-age=120') //2分鐘 復制代碼

    ??通過Pragma來禁止緩存,通過Cache-Control設置兩分鐘緩存,但是重新訪問我們會發現瀏覽器會再次發起一次請求,說明了Pragma的優先級高于Cache-Control

    關于Cache-Control

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

    • private: 客戶端可以緩存
    • public: 客戶端和代理服務器都可緩存
    • max-age=xxx: 緩存的內容將在 xxx 秒后失效
    • no-cache: 需要使用對比緩存來驗證緩存數據
    • no-store: 所有內容都不會緩存,強制緩存,對比緩存都不會觸發

    ??所以我們在刷新頁面的時候,如果只按F5只是單純的發送請求,按Ctrl+F5會發現請求頭上多了兩個字段Pragma: no-cache和Cache-Control: no-cache。

    緩存的優先級

    ??上面我們說過強制緩存的優先級高于協商緩存,Pragma的優先級高于Cache-Control,那么其他緩存的優先級順序怎么樣呢?網上查閱了資料得出以下順序(PS:有興趣的童鞋可以驗證一下正確性告訴我):

    Pragma > Cache-Control > Expires > ETag > Last-Modified

    如果覺得寫得還不錯,請關注我的掘金主頁。更多文章請訪問謝小飛的博客

    ??參考資料:

    http緩存優先級問題

    徹底弄懂HTTP緩存機制及原理

    HTTP緩存控制小結

    淺談瀏覽器http的緩存機制

    通過express框架簡單實踐幾種設置HTTP對緩存的控制

    創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

    總結

    以上是生活随笔為你收集整理的前端也要懂Http缓存机制的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。