浏览器缓存机制
1.概述
浏览器具有缓存的功能,缓存的本质是为了:
- 使用缓存能直接从本地读取数据,减少向服务器请求数据,缓解服务器压力。
- 使用缓存能提高浏览器的二次渲染的时间,以及减少用户的流量消耗。
2.缓存的形式
浏览器缓存分为两种:
1️⃣ 强缓存(直接命中本地缓存)
浏览器发送请求之前,会先查看本地是否缓存,如果命中本地缓存,则无需向服务器发送请求,直接用本地缓存中的数据。
2️⃣ 协商缓存(与服务器协商是否命中本都缓存)
如果缓存过期,浏览器则会发送一个请求给服务器,服务器拿到请求后会根据请求头中的几个字段来判断对比服务器上的资和浏览器上的缓存是否一致,若一致则浏览器可以继续使用本地的缓存,这就是命中了协商缓存,其结果就是给浏览器返回 304 状态码,告知该资源未发生变动,可以继续使用本地缓存。
3.缓存的类型
缓存分为两种缓存:
1️⃣ memory cache:内存缓存
特点:优先级高,存储在内存中,存活时间短,页面关闭就会释放这些资源。
2️⃣ disk cache:磁盘缓存
特点:优先级低,存储在硬盘中,存活时间长,一般用来存储css,图片等资源。
5.请求头设置
强缓存使用 Expires
和 Cache-Control
Expires
->http/1.0
- 用法
res.setHeader('Expires', 'Mon Jun 13 2022 22:59:00 GMT');
- 说明
设置一个时间点,告诉浏览器在这个时间点之前,请求资源的时候都使用缓存;该属性使用绝对值,客户端和服务器时间不一致会发生问题。 - 查看
# 查看响应头
$ curl -i/-I http://aaa.bbb.com
Cache-Control
->http/1.1
- 用法
public
private
no-cache
no-store
max-age=xxx
res.setHeader('Cache-Control', 'public,max-age=xxx');
- 说明
优先级比Expries
协商缓存使用两对 Last-Modified/if-Modified-Since
和 ETag/if-None-Math
Last-Modified/if-Modified-Since
- 用法
res.setHeader('Last-Modified', 'Mon Jun 13 2022 22:59:00 GMT');
- 说明
服务器设置Last-Modified
,用于告知客户端服务器该资源最后的修改时间。浏览器发起请求时带在if-Modified-Since
中带上该时间值,用于比对服务器上资源的最后修改时间,若相同则资源未修改,返回 304 使用协商缓存。若不同,则带上资源数据返回200
。 - 示例代码
app.get('/demo.js', (req, res) => {
let sourcePath = path.resolve(__dirname, 'aa.html'),
result = fs.readFileSync(sourcePath),
lastModified = 'Tue, 14 Jun 2022 04:02:18 GMT'
if (req.headers['if-modified-since'] === lastModified) {
res.writeHead(304, 'Not Modified')
res.end()
} else {
res.setHeader('cache-control', 'max-age=5') // 强缓存过期时间为5s,5s后的请求浏览器都会和服务器进行协商
res.setHeader('Last-Modified', lastModified)
res.writeHead(200, 'OK')
res.end(result)
}
})
ETag/if-None-Math
服务器返回资源时,每次都会在响应头中返回**ETag
** ,这是服务器用来表示该资源的唯一标识,如果文件发生改变,该标识也会改变。
浏览器在请求资源时,会在if-None-Math
中带上该资源的唯一标识,用于和服务器上资源进行对比,如果服务器上的资源为改变则返回状态码 304 告知客户端使用缓存,若资源发生改变则返回状态码 200 并带上新的资源。
- 示例代码
app.get('/demo.js', (req, res) => {
let sourcePath = path.resolve(__dirname, 'aa.html'),
result = fs.readFileSync(sourcePath),
etag = md5(md5)
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(result)
}
})