http 缓存分为强缓存和协商缓存。他们用于优化网站性能并减少服务器负载。这两种缓存都通过 HTTP 响应头来控制,它们分别基于不同的缓存验证方式,可以根据资源的特性和需求来选择合适的缓存策略。
客户端
html
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<button id="btn">send</button>
<script>
const btn = document.querySelector('#btn')
btn.addEventListener('click', ()=> {
fetch('http://localhost:3000/api3')
})
</script>
</body>
</html>
1. 强缓存(Strong Cache):
- 客户端在请求资源时,会检查缓存的相关响应头(如
Cache-Control
、Expires
),如果资源的缓存尚未过期,客户端将直接从本地缓存中获取资源,而不会发送请求到服务器。 - 常用的强缓存响应头包括:
Cache-Control: max-age=<seconds>
:指定资源的缓存时间,单位为秒。Expires: <date>
:指定资源的过期时间,是一个日期字符串。它是一个 HTTP 1.0 的头部字段,但仍然被一些客户端和服务器使用。
- 从浏览器读取缓存分为内存缓存(memory cache,浏览器内存)和硬盘缓存(disk cache,计算机硬盘,空间大,读取效率低),而这两种缓存策略由浏览器自身分配
- 状态码为 200
js
import express from 'express'
import cors from 'cors'
import fs from 'node:fs'
import crypto from 'node:crypto'
const app = express()
app.use(cors())
//静态资源缓存 html, css, js, png
// app.use(express.static('./static', {
// maxAge: 100 * 60 * 5,
// lastModified: true
// }))
//动态资源缓存
//Expires
app.get('/api', (req, res) => {
// 到了过期时间之后将不再进行缓存
res.setHeader('Expires', new Date('2024-4-6 8:57:00').toUTCString())
res.send('Expires1111')
})
// Cache-Control
// public 任何服服务器都可以缓存包括代理服务器 cdn
// private 只能浏览器缓存 不包括代理服务器
// max-age 缓存的时间
app.get('/api2', (req, res) => {
res.setHeader('Cache-Control', 'public, max-age=10')
res.send('Cache-Control1111')
})
app.listen(3000, () => {
console.log('3000端口已启用')
})
- 当 expires 和 cache-control 同时出现,cache-control 的 max-age 优先级更高
2. 协商缓存(Conditional Cache):
- 协商缓存是指客户端在请求资源时,需要与服务器进行通信来验证缓存的有效性。
- 常用的协商缓存响应头包括:
Last-Modified
:指定资源的最后修改时间。ETag
:指定资源的实体标签(根据内容生成一个哈希),是一个唯一标识符。
- 当客户端发送包含
If-Modified-Since
或If-None-Match
的请求头时,服务器会根据这些信息来判断资源是否已经发生变化,如果未发生变化,服务器将返回304 Not Modified
响应,并告知客户端使用缓存。 - 强缓存和协商缓存同时出现,默认强缓存的优先级更高(当然也可以通过 Cache-Control 响应头进行控制),如果强缓存未命中(例如
max-age
过期),也会发起协商缓存的请求 - ETag 优先级比 Last-Modified 高
- 状态码为 304
js
import express from 'express'
import cors from 'cors'
import fs from 'node:fs'
import crypto from 'node:crypto'
const app = express()
app.use(cors())
//获取文件的最后修改时间
const getFileModifyTime = ()=> {
return fs.statSync('./index.js').mtime.toISOString()
}
app.get('/api3', (req,res)=> {
// no-cache 使用协商缓存而不是强缓存
// no-store 不走任何缓存
res.setHeader('Cache-Control' ,'no-cache')
const modifyTime = getFileModifyTime();
// 浏览器根据 Last-Modified 的值自动设置 if-modified-since 的值(与上一次 Last-Modified 的值相同)
const ifModifiedSince = req.headers['if-modified-since']
// 如果文件不修改则不用重新缓存
if (ifModifiedSince === modifyTime) {
console.log('缓存了')
res.statusCode = 304
res.end()
return
}
console.log('没有缓存')
res.setHeader('Last-Modified', modifyTime)
res.send('Last-Modified111')
})
app.listen(3000, () => {
console.log('3000端口已启用')
})
js
import express from 'express'
import cors from 'cors'
import fs from 'node:fs'
import crypto from 'node:crypto'
const app = express()
app.use(cors())
//获取文件内容哈希
const getFileHash = ()=> {
return crypto.createHash('sha256').update(fs.readFileSync('index.js')).digest('hex')
}
app.get('/api3', (req,res)=> {
// no-cache 使用协商缓存而不是强缓存
// no-store 不走任何缓存
res.setHeader('Cache-Control' ,'no-cache')
const fileHash = getFileHash();
const ifNoneMatch = req.headers['if-none-match']
// 如果文件不修改则不用重新缓存
if (ifNoneMatch === fileHash) {
console.log('缓存了')
res.statusCode = 304
res.end()
return
}
console.log('没有缓存')
res.setHeader('ETag', fileHash)
res.send('Etag111')
})
app.listen(3000, () => {
console.log('3000端口已启用')
})
强缓存和协商缓存可以结合使用,以提高缓存效果。通常情况下,可以首先使用强缓存来尽可能减少对服务器的请求,如果资源已经过期或者需要重新验证,则使用协商缓存来验证缓存的有效性。这样可以在保证性能的同时,确保客户端始终能够获取到最新的资源。