這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助
前言
我們打開百度這個網站并重繪多次時時,注意到百度的logo是沒有每次都加載一遍的,我們知道圖片是img標簽中的src屬性加載出來的,這也需要瀏覽器去請求圖片資源的,那么為什么重繪多次瀏覽器只請求了一次圖片資源呢?這就涉及到了瀏覽器的快取策略了,這張圖片被瀏覽器快取下來了!
正文
一、為什么要有瀏覽器的快取策略?
- 提升用戶體驗,減少頁面重復的http請求
二、為什么通過瀏覽器url地址欄訪問的html頁面不快取?
- 當 強制重繪頁面 或 瀏覽器url地址欄訪問資源 時,瀏覽器
默認會在請求頭中設定Cache-control: no-cache,如有設定該屬性瀏覽器就會忽略回應頭中的 Cache-control
如何優化網路資源請求的時間呢?有以下三種方式,
三、CDN網路分發
CDN:CDN會通過負載均衡技術,將用戶的請求定向到最合適的快取服務器上去獲取內容,
比如說,北京的用戶,我們讓他訪問北京的節點,深圳的用戶,我們讓他訪問深圳的節點,通過就近訪問,加速用戶對網站的訪問,進而解決Internet網路擁堵狀況,提高用戶訪問網路的回應速度,
四、強快取
強快取是瀏覽器的快取策略,后端設定回應頭中的屬性值就能設定檔案資源在瀏覽器的快取時間,過了快取的有效期再次訪問時,檔案資源需再次加載,
強快取有兩種方式來控制資源被瀏覽器快取的時長:
- 后端設定回應頭中的 Cache-control: max-age=3600 來控制快取時長(為一個小時)
- 后端設定回應頭中的 Expires:xxx 來控制快取的截止日期(截止日期為xxx)
我們直接上代碼讓你更好理解,我們需要實作一個頁面,頁面上需展現一個標題及一張圖片:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <h1>Earth</h1> <img src="https://www.cnblogs.com/smileZAZ/archive/2023/07/10/assets/image/earth.jpeg" alt=""> </body> </html>
const http = require('http');
const path = require('path');
const fs = require('fs');
const mime = require('mime'); //接收一個檔案后綴,回傳一個檔案型別
const server = http.createServer((req, res) => {
const filePath = path.resolve(__dirname, `www/${req.url}`) //resolve合并地址
if (fs.existsSync(filePath)) { //判斷路徑是否有效
const stats = fs.statSync(filePath) //獲取檔案資訊
const isDir = stats.isDirectory() //是否是檔案夾
if (isDir) {
filePath = path.join(filePath, 'index.html')
}
//讀取檔案
if (!isDir || fs.existsSync(filePath)) {
//判斷前端請求的路徑的后綴名是圖片還是文本
const { ext } = path.parse(filePath) //.html .jpeg
const time = new Date(Date.now() + 3600000).toUTCString() //定義時間 作為快取時間的有效期
let status = 200
res.writeHead(status, {
'Content-Type': `${mime.getType(ext)};charset=utf-8`,
'Cache-control': 'max-age=3600', //快取時長為一小時
// 'expires': time //截止日期 快取一小時后過期
})
if (status === 200) {
const fileStream = fs.createReadStream(filePath) //將檔案讀成流型別
fileStream.pipe(res) //將檔案流匯入回應體
}else{
res.end();
}
}
}
})
server.listen(3000, () => {
console.log('listening on port 3000');
})
第一次運行:

重繪頁面后,可以看到圖片資源沒有重新加載:

三、協商快取
我們想象這樣的場景:當我們偷偷把圖片偷偷換成另一張圖片,圖片名依然和之前那張一樣,會是什么結果呢?
操作后,重繪頁面發現圖片還是之前那張圖片,并沒有換成新的!那這就出事兒了,后端圖片換了,用戶看到的還是老圖片,有一種方案是改變圖片資源的名字,直接請求最新圖片資源,但這并不是最優方案,終極方案是需要協商快取的幫忙,
協商快取也是瀏覽器的快取策略,它也有兩種方式輔助強快取,來判斷檔案資源是否被修改:
1. 后端設定回應頭中的 last-modified: xxxx
- 輔助強快取,讓URL地址欄請求的資源也能被快取
- 輔助強快取,借助請求頭中的if-modified-since來判斷資源檔案是否被修改,如果被修改則回傳新的資源,否則回傳304狀態碼,讓前端讀取本地快取
代碼如下:
const http = require('http');
const path = require('path');
const fs = require('fs');
const mime = require('mime'); //接收一個檔案后綴,回傳一個檔案型別
const server = http.createServer((req, res) => {
const filePath = path.resolve(__dirname, `www/${req.url}`) //resolve合并地址
if (fs.existsSync(filePath)) { //判斷路徑是否有效
const stats = fs.statSync(filePath) //獲取檔案資訊
const isDir = stats.isDirectory() //是否是檔案夾
if (isDir) {
filePath = path.join(filePath, 'index.html')
}
//讀取檔案
if (!isDir || fs.existsSync(filePath)) {
//判斷前端請求的路徑的后綴名是圖片還是文本
const { ext } = path.parse(filePath) //.html .jpeg
const time = new Date(Date.now() + 3600000).toUTCString() //定義時間 作為快取時間的有效期
const timeStamp = req.headers['if-modified-since'] //請求頭的if-modified-since欄位
let status = 200
//判斷檔案是否修改過
if (timeStamp && Number(timeStamp) === stats.mtimeMs) { //timeStamp為字串 轉換為number型別判斷
status = 304
}
res.writeHead(status, {
'Content-Type': `${mime.getType(ext)};charset=utf-8`,
'Cache-control': 'max-age=3600', //快取時長為一小時 //max-age=0或no-cache不需要快取
// 'expires': time //截止日期 快取一小時后過期
'last-modified': stats.mtimeMs //檔案最后一次修改時間
})
if (status === 200) {
const fileStream = fs.createReadStream(filePath) //將檔案讀成流型別
fileStream.pipe(res) //將檔案流匯入回應體
}else{
res.end();
}
}
}
})
server.listen(3000, () => {
console.log('listening on port 3000');
})
我們只要對比last-modified的值和if-modified-since的值有無變化即可:
2. Etag:檔案的標簽
- 請求頭中會被攜帶If-None-Match
- Etag保證了每一個資源是唯一的,資源變化都會導致Etag變化,服務器根據If-None-Match值來判斷是否命中快取, 當服務器回傳304的回應時,由于ETag重新生成過,response header中還會把這個ETag回傳,即使這個ETag跟之前的沒有變化,

代碼如下:
const http = require('http');
const path = require('path');
const fs = require('fs');
const mime = require('mime'); //接收一個檔案后綴,回傳一個檔案型別
const md5 = require('crypto-js/md5');
const server = http.createServer((req, res) => {
const filePath = path.resolve(__dirname, `www/${req.url}`) //resolve合并地址
if (fs.existsSync(filePath)) { //判斷路徑是否有效
const stats = fs.statSync(filePath) //獲取檔案資訊
const isDir = stats.isDirectory() //是否是檔案夾
if (isDir) {
filePath = path.join(filePath, 'index.html')
}
//讀取檔案
if (!isDir || fs.existsSync(filePath)) {
//判斷前端請求的路徑的后綴名是圖片還是文本
const { ext } = path.parse(filePath) //.html .jpeg
const content = fs.readFileSync(filePath);
let status = 200
//判斷檔案是否被修改過
if (req.headers['if-none-match'] == md5(content)) {
status=304
}
res.writeHead(status, {
'Content-Type': `${mime.getType(ext)};charset=utf-8`,
'Cache-control': 'max-age=3600', //快取時長為一小時 //max-age=0或no-cache不需要快取
'Etag': md5(content) //檔案資源的md5值
})
if (status === 200) {
const fileStream = fs.createReadStream(filePath) //將檔案讀成流型別
fileStream.pipe(res) //將檔案流匯入回應體
} else {
res.end();
}
}
}
})
server.listen(3000, () => {
console.log('listening on port 3000');
})
結
最后附上一張圖便于更好理解瀏覽器的快取策略:

本文轉載于:
https://juejin.cn/post/7253675120010199101
如果對您有所幫助,歡迎您點個關注,我會定時更新技術檔案,大家一起討論學習,一起進步,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/556975.html
標籤:其他
上一篇:JS中使用Promise.all控制所有的異步請求都完成后,在執行后續邏輯
下一篇:返回列表

