<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>

          【總結(jié)】2058- 前端需要知道的緩存知識總結(jié)

          共 8503字,需瀏覽 18分鐘

           ·

          2024-05-26 18:15

             
          引言??

          HTTP緩存是一種用于提高網(wǎng)站性能和減少帶寬使用的技術(shù)。當(dāng)用戶訪問一個網(wǎng)頁時,瀏覽器會下載頁面上的所有資源(如HTML、CSS、JavaScript等),這些資源會占用大量的帶寬和時間。為了減少這些資源的加載時間,HTTP緩存機(jī)制被引入。???

          緩存分為強緩存協(xié)商緩存兩種,強緩存不能緩存地址欄訪問的文件,協(xié)商緩存可以緩存地址欄訪問的文件。

          1、強緩存??

          由服務(wù)器設(shè)置過期時間,在該時間到期之前,瀏覽器會直接從本地緩存中獲取資源。

          強緩存的實現(xiàn)方式有兩種:ExpiresCache-Control

          1.1、Expires??????????

          Expires 是 HTTP 緩存機(jī)制中實現(xiàn)強緩存的一種方式,它通過在響應(yīng)頭部中加入一個過期時間來控制緩存。Expires 的值是一個日期,格式為:Wed, 21 Oct 2015 07:28:00 GMT。它表示該資源的過期時間。當(dāng)瀏覽器再次請求該資源時,會判斷是否在該過期時間內(nèi),如果是則直接從緩存中獲取資源,否則重新向服務(wù)器請求。

          要設(shè)置 Expires 頭部,需要在服務(wù)器端進(jìn)行配置。例如,在 Nginx 中可以使用expires指令來設(shè)置過期時間:

          location / {
          expires 1h;
          }

          注意??:由于Expires是基于客戶端時間計算的,如果客戶端的時間與服務(wù)器的時間不一致,則可能會影響緩存效果。

          1.2、Cache-Control??????????

          Cache-Control 是通過在響應(yīng)頭部中加入 Cache-Control 字段,并設(shè)置max-age值來表示該資源在多少秒內(nèi)有效(即緩存的最大時長)。

          Cache-Control響應(yīng)頭的最常用格式為:

          Cache-Control: max-age=<seconds> // seconds 是緩存的時間,單位是秒

          當(dāng)瀏覽器請求資源得到的響應(yīng)頭中帶有 Cache-Control 響應(yīng)頭時,瀏覽器會將該資源緩存到本地。在下一次訪問該資源時,同時滿足下述條件,瀏覽器就會使用本地資源(即緩存),而不重新去服務(wù)器請求該資源:

          1. 緩存時間未過期
          2. URL未發(fā)生變化
          3. 請求頭中沒有 Cache-Control: no-cachePragma: no-cache 這兩個信息(強制刷新會在請求頭中添加 Cache-Control: no-cache

          注意??:直接通過瀏覽器的地址欄訪問的資源緩存會失效(跟強制刷新一樣會在請求頭中添加 Cache-Control: no-cache

          接下來我們通過一個簡單的頁面來實踐一下:

          目錄結(jié)構(gòu),我們準(zhǔn)備兩個文件 index.jsindex.html ,再準(zhǔn)備兩張圖片:

          http
          |--- index.js
          |--- index.html
          |--- 1.jpg
          |___ 2.jpg

          index.js

          提供一個 node 服務(wù),用于返回瀏覽器請求的資源(index.html 和圖片)

          // index.js
          const http = require('http')
          const fs = require('fs')
          const path = require('path')

          const server = http.createServer((req, res) => {

            let filePath = path.resolve(__dirname, req.url === '/' ? `index.html` : '1.jpg')

            res.writeHead(200, {
              'Content-Type': req.url === '/' ? 'text/html; charset=utf-8' : 'image/png',
              'Cache-Control''max-age=86400'// 緩存一天
            })
            const fileStream = fs.createReadStream(filePath)
            return fileStream.pipe(res)
          })

          server.on('clientError', (err, socket) => {
            socket.end('HTTP/1.1 400 Bad Request\r\n\r\n')
          })

          server.listen(8080, () => {
            console.log(`opened server on http://localhost:${server.address().port}`)
          })

          index.html

          一個簡單的頁面,包含一張圖片,因為我們會直接通過瀏覽器的地址欄訪問 html,所以 html 的緩存策略會失效。這里我們判斷緩存是否生效是通過頁面中的圖片去判斷的。

          <!-- index.html -->
          <!DOCTYPE html>
          <html lang="en">
          <head>
            <meta charset="UTF-8">
            <meta http-equiv="X-UA-Compatible" content="IE=edge">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>Hello World</title>
          </head>
          <body>
            <h1>Hello World!</h1>
            <!-- 別忘記替換成你的圖片名稱 -->
            <img src="./1.jpg" title="123">
          </body>
          </html>

          然后隨便準(zhǔn)備兩張圖片就可以了,我們在項目的跟目錄使用 node index.js 來啟動項目。如下圖提示,就表示啟動成功,然后我們通過瀏覽器訪問 http://localhost:8080/ 就能看到我們的頁面了。

          image.png

          首次請求 我們主要看圖片的請求頭跟響應(yīng)頭就行(因為html的緩存會失效)。

          image.png
          image.png

          刷新頁面(非強制刷新)

          第二次的請求可以看到請求的 Size 變成了 memory cache,并且 Time 也變?yōu)榱?0。再進(jìn)一步看請求頭和響應(yīng)頭,請求頭中的 Cache-Control: no-cache 屬性沒有了,并且瀏覽器會給出一個警告:Provisional headers are shown. Disable cache to see full headers.。大致意思就是當(dāng)前使用的是緩存的臨時文件,禁用緩存后才可以查看完整的請求頭。

          image.png
          image.png

          驗證緩存

          上述的方法可能并不一定讓你相信我們使用的是緩存文件,而不是重新請求的資源文件。

          一開始我們準(zhǔn)備了兩張圖片,現(xiàn)在使用的是 1.jpg,還有一個 2.jpg,我們把 1.jpg 刪除了,然后把2.jpg 改名成 1.jpg,然后刷新頁面(非強制刷新),就會發(fā)現(xiàn)雖然我們圖片更改了,但是圖片并不是我們后面改名的那個圖片,還是之前的圖片。

          image.png

          強制刷新后就能看到,我們替換的圖片生效了,請求頭中也帶上了 Cache-Control: no-cache 屬性。

          image.png

          2、協(xié)商緩存

          利用瀏覽器和服務(wù)器之間的通信來確定是否需要重新獲取資源。

          協(xié)商緩存有兩種實現(xiàn)方式:If-Modified-SinceETag

          當(dāng)瀏覽器第一次請求資源時,服務(wù)器會返回該資源的 ETag 值和 Last-Modified 時間。當(dāng)瀏覽器再次請求該資源時,它會將這些值作為請求頭部的 If-Modified-SinceIf-None-Match 字段發(fā)送給服務(wù)器。服務(wù)器會比較這些值與資源的當(dāng)前狀態(tài),如果資源沒有發(fā)生變化,服務(wù)器返回 304 Not Modified 響應(yīng),告訴瀏覽器可以使用緩存的資源。

          如果資源已經(jīng)發(fā)生了變化,服務(wù)器會返回新的資源,并更新 ETagLast-Modified

          2.1、If-Modified-Since ??????????

          利用響應(yīng)頭的 Last-Modified 來設(shè)置緩存,并在下次請求的請求頭中攜帶 If-Modified-Since 來判斷該資源是否發(fā)生變化,如果發(fā)生變化則返回新的資源,并更新 Last-Modified 屬性,如果沒有發(fā)生變化,則返回 304 跟空的 body

          對強緩存的例子稍微修改一下

          // index.js
          const http = require('http')
          const fs = require('fs')
          const path = require('path')

          const server = http.createServer((req, res) => {
            let filePath = path.resolve(__dirname, req.url === '/' ? `index.html` : '1.jpg')
            const stat = fs.statSync(filePath)
            const lastModified = stat.mtime.toUTCString()
            const header = {
              'Content-Type': req.url === '/' ? 'text/html; charset=utf-8' : 'image/png',
              'Last-Modified': lastModified
            }
            // 判斷資源是否發(fā)生變化
            if (req.headers['if-modified-since'] === lastModified) {
              res.writeHead(304, header)
              res.end()
            } else {
              res.writeHead(200, header)
              const fileStream = fs.createReadStream(filePath)
              return fileStream.pipe(res)
            }
          })

          server.on('clientError', (err, socket) => {
            socket.end('HTTP/1.1 400 Bad Request\r\n\r\n')
          })

          server.listen(8080, () => {
            console.log(`opened server on http://localhost:${server.address().port}`)
          })

          首次請求

          響應(yīng)頭攜帶 Last-Modified: Mon, 05 Jun 2023 08:57:17 GMT 屬性,告訴瀏覽器這個文件需要緩存。

          image.png

          刷新頁面(非強制刷新)

          第二次請求,響應(yīng)狀態(tài)碼變?yōu)?304,并在請求頭中攜帶 If-Modified-Since: Mon, 05 Jun 2023 08:57:17 GMT 屬性,表示瀏覽器使用緩存文件。

          image.png

          改變html文件

          把 html 中 的 Hello World! 改為 Web Html,并刷新頁面(非強制刷新),發(fā)現(xiàn)響應(yīng)狀態(tài)碼變?yōu)?200 ,并且更新了頁面和響應(yīng)頭的 Last-Modified 屬性。

          image.png

          注意??:如果資源的修改時間只精確到秒,而不是毫秒,可能會導(dǎo)致緩存失效。此外,如果服務(wù)器上的資源被修改了,但修改時間沒有更新,也會導(dǎo)致緩存失效

          2、ETag ??????????

          ETag 基本上與 If-Modified-Since 一致, 利用響應(yīng)頭的 Etag 來設(shè)置緩存,并在下次請求的請求頭中攜帶 if-none-match 來判斷該資源是否發(fā)生變化,如果發(fā)生變化則返回新的資源,并更新 Etag 屬性,如果沒有發(fā)生變化,則返回 304 跟空的 body

          const http = require('http')
          const fs = require('fs')
          const path = require('path')
          const crypto = require('crypto')

          const server = http.createServer((req, res) => {
            let filePath = path.resolve(__dirname, req.url === '/' ? `index.html` : '1.jpg')
            const fileContent = fs.readFileSync(filePath)
            const hash = crypto.createHash('md5').update(fileContent).digest('hex')
            const etag = `"${hash}"` // etag需要加雙引號
            const header = {
              'Content-Type': req.url === '/' ? 'text/html; charset=utf-8' : 'image/png',
              'Etag': etag
            }
            if (req.headers['if-none-match'] === etag) {
              res.writeHead(304, header)
              res.end()
            } else {
              res.writeHead(200, header)
              const fileStream = fs.createReadStream(filePath)
              return fileStream.pipe(res)
            }
          })

          server.on('clientError', (err, socket) => {
            socket.end('HTTP/1.1 400 Bad Request\r\n\r\n')
          })

          server.listen(8080, () => {
            console.log(`opened server on http://localhost:${server.address().port}`)
          })

          這里的測試證明就不寫,不然這文章的字?jǐn)?shù)就太水了,如果你們有興趣可以自己嘗試一下

          原文:https://juejin.cn/post/7241095368179957820

          作者:——樹深遇鹿


          瀏覽 59
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <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>
                  无码人妻一级毛片免费 | 欧美人妻操逼 | 中文无码短视频 | 美女扒开粉嫩的尿囗给男生桶 | 精品人兽|