<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          圖解:徹底弄懂HTTP緩存機(jī)制及原理

          共 5346字,需瀏覽 11分鐘

           ·

          2021-04-10 10:18

            前言


          HTTP 的緩存機(jī)制,可以說(shuō)這是前端工程師需要掌握的重要知識(shí)點(diǎn)之一。本文將針對(duì) HTTP 緩存整體的流程做一個(gè)詳細(xì)的講解,爭(zhēng)取做到大家讀完整篇文章后,對(duì)緩存有一個(gè)整體的了解。

          HTTP 緩存分為 2 種,一種是強(qiáng)緩存,另一種是協(xié)商緩存。主要作用是可以加快資源獲取速度,提升用戶體驗(yàn),減少網(wǎng)絡(luò)傳輸,緩解服務(wù)端的壓力。這是緩存運(yùn)作的一個(gè)整體流程圖:
          Http緩存.jpg

          強(qiáng)緩存

          不需要發(fā)送請(qǐng)求到服務(wù)端,直接讀取瀏覽器本地緩存,在 Chrome 的 Network 中顯示的 HTTP 狀態(tài)碼是 200 ,在 Chrome 中,強(qiáng)緩存又分為 Disk Cache(存放在硬盤中)和 Memory Cache(存放在內(nèi)存中),存放的位置是由瀏覽器控制的。是否強(qiáng)緩存由 Expires、Cache-Control 和 Pragma 3 個(gè) Header 屬性共同來(lái)控制。

          ○ Expires

          Expires 的值是一個(gè) HTTP 日期,在瀏覽器發(fā)起請(qǐng)求時(shí),會(huì)根據(jù)系統(tǒng)時(shí)間和 Expires 的值進(jìn)行比較,如果系統(tǒng)時(shí)間超過(guò)了 Expires 的值,緩存失效。由于和系統(tǒng)時(shí)間進(jìn)行比較,所以當(dāng)系統(tǒng)時(shí)間和服務(wù)器時(shí)間不一致的時(shí)候,會(huì)有緩存有效期不準(zhǔn)的問(wèn)題。Expires 的優(yōu)先級(jí)在三個(gè) Header 屬性中是最低的。

          ○ Cache-Control

          Cache-Control 是 HTTP/1.1 中新增的屬性,在請(qǐng)求頭和響應(yīng)頭中都可以使用,常用的屬性值如有:

          • max-age:?jiǎn)挝皇敲?,緩存時(shí)間計(jì)算的方式是距離發(fā)起的時(shí)間的秒數(shù),超過(guò)間隔的秒數(shù)緩存失效
          • no-cache:不使用強(qiáng)緩存,需要與服務(wù)器驗(yàn)證緩存是否新鮮
          • no-store:禁止使用緩存(包括協(xié)商緩存),每次都向服務(wù)器請(qǐng)求最新的資源
          • private:專用于個(gè)人的緩存,中間代理、CDN 等不能緩存此響應(yīng)
          • public:響應(yīng)可以被中間代理、CDN 等緩存
          • must-revalidate:在緩存過(guò)期前可以使用,過(guò)期后必須向服務(wù)器驗(yàn)證

          ○ Pragma

          Pragma 只有一個(gè)屬性值,就是 no-cache ,效果和 Cache-Control 中的 no-cache 一致,不使用強(qiáng)緩存,需要與服務(wù)器驗(yàn)證緩存是否新鮮,在 3 個(gè)頭部屬性中的優(yōu)先級(jí)最高。

          本地通過(guò) express 起一個(gè)服務(wù)來(lái)驗(yàn)證強(qiáng)緩存的 3 個(gè)屬性,代碼如下:
          const express = require('express');
          const app = express();
          var options = { 
            etagfalse// 禁用協(xié)商緩存
            lastModified: false// 禁用協(xié)商緩存
            setHeaders: (res, path, stat) => {
              res.set('Cache-Control''max-age=10'); // 強(qiáng)緩存超時(shí)時(shí)間為10秒
            },
          };
          app.use(express.static((__dirname + '/public'), options));
          app.listen(3000);

          第一次加載,頁(yè)面會(huì)向服務(wù)器請(qǐng)求數(shù)據(jù),并在 Response Header 中添加 Cache-Control ,過(guò)期時(shí)間為 10 秒。

          緩存1.jpg
          第二次加載,Date 頭屬性未更新,可以看到瀏覽器直接使用了強(qiáng)緩存,實(shí)際沒有發(fā)送請(qǐng)求。
          緩存2.jpg
          過(guò)了 10 秒的超時(shí)時(shí)間之后,再次請(qǐng)求資源:
          緩存3.jpg
          當(dāng) Pragma 和 Cache-Control 同時(shí)存在的時(shí)候,Pragma 的優(yōu)先級(jí)高于 Cache-Control。
          緩存5.jpg

          協(xié)商緩存

          當(dāng)瀏覽器的強(qiáng)緩存失效的時(shí)候或者請(qǐng)求頭中設(shè)置了不走強(qiáng)緩存,并且在請(qǐng)求頭中設(shè)置了If-Modified-Since 或者 If-None-Match 的時(shí)候,會(huì)將這兩個(gè)屬性值到服務(wù)端去驗(yàn)證是否命中協(xié)商緩存,如果命中了協(xié)商緩存,會(huì)返回 304 狀態(tài),加載瀏覽器緩存,并且響應(yīng)頭會(huì)設(shè)置 Last-Modified 或者 ETag 屬性。

          ○ ETag/If-None-Match

          ETag/If-None-Match 的值是一串 hash 碼,代表的是一個(gè)資源的標(biāo)識(shí)符,當(dāng)服務(wù)端的文件變化的時(shí)候,它的 hash碼會(huì)隨之改變,通過(guò)請(qǐng)求頭中的 If-None-Match 和當(dāng)前文件的 hash 值進(jìn)行比較,如果相等則表示命中協(xié)商緩存。ETag 又有強(qiáng)弱校驗(yàn)之分,如果 hash 碼是以 "W/" 開頭的一串字符串,說(shuō)明此時(shí)協(xié)商緩存的校驗(yàn)是弱校驗(yàn)的,只有服務(wù)器上的文件差異(根據(jù) ETag 計(jì)算方式來(lái)決定)達(dá)到能夠觸發(fā) hash 值后綴變化的時(shí)候,才會(huì)真正地請(qǐng)求資源,否則返回 304 并加載瀏覽器緩存。

          ○ Last-Modified/If-Modified-Since

          Last-Modified/If-Modified-Since 的值代表的是文件的最后修改時(shí)間,第一次請(qǐng)求服務(wù)端會(huì)把資源的最后修改時(shí)間放到 Last-Modified 響應(yīng)頭中,第二次發(fā)起請(qǐng)求的時(shí)候,請(qǐng)求頭會(huì)帶上上一次響應(yīng)頭中的 Last-Modified 的時(shí)間,并放到 If-Modified-Since 請(qǐng)求頭屬性中,服務(wù)端根據(jù)文件最后一次修改時(shí)間和 If-Modified-Since 的值進(jìn)行比較,如果相等,返回 304 ,并加載瀏覽器緩存。

          本地通過(guò) express 起一個(gè)服務(wù)來(lái)驗(yàn)證協(xié)商緩存,代碼如下:
          const express = require('express');
          const app = express();
          var options = { 
            etagtrue// 開啟協(xié)商緩存
            lastModified: true// 開啟協(xié)商緩存
            setHeaders: (res, path, stat) => {
              res.set({
                'Cache-Control''max-age=00'// 瀏覽器不走強(qiáng)緩存
                'Pragma''no-cache'// 瀏覽器不走強(qiáng)緩存
              });
            },
          };
          app.use(express.static((__dirname + '/public'), options));
          app.listen(3001);

          第一次請(qǐng)求資源:

          緩存6.jpg
          第二次請(qǐng)求資源,服務(wù)端根據(jù)請(qǐng)求頭中的 If-Modified-Since 和 If-None-Match 驗(yàn)證文件是否修改。
          緩存7.jpg

          我們?cè)賮?lái)驗(yàn)證一下 ETag 在強(qiáng)校驗(yàn)的情況下,只增加一行空格,hash 值如何變化,在代碼中,我采用的是對(duì)文件進(jìn)行 MD5 加密來(lái)計(jì)算其 hash 值。

          注:只是為了演示用,實(shí)際計(jì)算不是通過(guò) MD5 加密的,Apache 默認(rèn)通過(guò) FileEtag 中 FileEtag INode Mtime Size 的配置自動(dòng)生成 ETag,用戶可以通過(guò)自定義的方式來(lái)修改文件生成 ETag 的方式。

          為了保證 lastModified 不影響緩存,我把通過(guò) Last-Modified/If-Modified-Since 請(qǐng)求頭刪除了,源碼如下:
          const express = require('express');
          const CryptoJS = require('crypto-js/crypto-js');
          const fs = require('fs');
          const app = express();
          var options = { 
            etagtrue// 只通過(guò)Etag來(lái)判斷
            lastModified: false// 關(guān)閉另一種協(xié)商緩存
            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'// 瀏覽器不走強(qiáng)緩存
                'Pragma''no-cache'// 瀏覽器不走強(qiáng)緩存
                'ETag': hash, // 手動(dòng)設(shè)置Etag值為MD5加密后的hash值
              });
            },
          };
          app.use(express.static((__dirname + '/public'), options));
          app.listen(4000); // 使用新端口號(hào),否則上面驗(yàn)證的協(xié)商緩存會(huì)一直存在

          第一次和第二次請(qǐng)求如下:

          緩存10.jpg
          緩存11.jpg
          然后我修改了 test.js ,增加一個(gè)空格后再刪除一個(gè)空格,保持文件內(nèi)容不變,但文件的修改時(shí)間改變,發(fā)起第三次請(qǐng)求,由于我生成 ETag 的方式是通過(guò)對(duì)文件內(nèi)容進(jìn)行 MD5 加密生成,所以雖然修改時(shí)間變化了,但請(qǐng)求依然返回了 304,讀取瀏覽器緩存。
          緩存13.jpg

          ETag/If-None-Match 的出現(xiàn)主要解決了 Last-Modified/If-Modified-Since 所解決不了的問(wèn)題:

          • 如果文件的修改頻率在秒級(jí)以下,Last-Modified/If-Modified-Since 會(huì)錯(cuò)誤地返回 304
          • 如果文件被修改了,但是內(nèi)容沒有任何變化的時(shí)候,Last-Modified/If-Modified-Since 會(huì)錯(cuò)誤地返回 304,上面的例子就說(shuō)明了這個(gè)問(wèn)題

          總結(jié)

          在實(shí)際使用場(chǎng)景中,比如政采云的官網(wǎng)。圖片、不常變化的 JS 等靜態(tài)資源都會(huì)使用緩存來(lái)提高頁(yè)面的加載速度。例如政采云首頁(yè)的頂部導(dǎo)航欄,埋點(diǎn) SDK 等等。

          在文章的最后,我們?cè)俅位氐竭@張流程圖,這張圖涵蓋了 HTTP 緩存的整體流程,大家對(duì)整體流程熟悉后,也可以自己動(dòng)手通過(guò) Node 來(lái)驗(yàn)證下 HTTP 緩存。
          Http緩存.jpg


          最后



          如果你覺得這篇內(nèi)容對(duì)你挺有啟發(fā),我想邀請(qǐng)你幫我三個(gè)小忙:

          1. 點(diǎn)個(gè)「在看」,讓更多的人也能看到這篇內(nèi)容(喜歡不點(diǎn)在看,都是耍流氓 -_-)

          2. 歡迎加我微信「qianyu443033099」拉你進(jìn)技術(shù)群,長(zhǎng)期交流學(xué)習(xí)...

          3. 關(guān)注公眾號(hào)「前端下午茶」,持續(xù)為你推送精選好文,也可以加我為好友,隨時(shí)聊騷。

          點(diǎn)個(gè)在看支持我吧,轉(zhuǎn)發(fā)就更好了

          瀏覽 44
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产成人无码精品久久久一区 | 天天操妹子 | 国产乱人乱偷精品视频a人人澡 | 国产多毛 | 国产a片偷拍网站视频 |