浏览器缓存:缓存协商(协商缓存)和彻底缓存(强缓存)
1、强制缓存:不会向服务器发送请求,直接从缓存中读取资源,在chrome控制台的network选项中可以看到该请求返回200的状态码。
2、协商缓存:在使用本地缓存之前,需要向服务器发送请求,服务器会根据这个请求的request header的一些参数来判断是否命中协商缓存,如果命中,则返回304状态码并带上新的response header通知浏览器从缓存中读取资源;协商缓存可以解决强制缓存的情况下,资源不更新的问题。
两者的共同特点:都是从客户端缓存中读取资源,区别是强缓存不会发请求,协商缓存会发请求。
强制缓存中header的参数(响应头)
Expires:response header里的过期时间,浏览器再次加载资源时,如果在这个过期时间内则命中强缓存。
Cache-Control:当值设为max-age=300时,则代表在这个请求正确返回时间(浏览器也会记录下来)的五分钟内再次加载资源,则会命中强缓存。
Cache-Control:
- 在 Response Headers 中。
- 控制强制缓存的逻辑。
- 例如 Cache-Control: max-age=3153600(单位是秒)
Cache-Control 有哪些值:
- max-age:缓存最大过期时间。
- no-cache:可以在客户端存储资源,每次都必须去服务端做新鲜度校验,来决定从服务端获取新的资源(200)还是使用客户端缓存(304)。
- no-store:永远都不要在客户端存储资源,永远都去原始服务器去获取资源。
function send(req, res, filepath) {
res.setHeader('Content-Type', mime.getType(filepath))
res.setHeader('Expires', new Date(Date.now() + 30 * 1000).toUTCString())
res.setHeader('Cache-Control', 'max-age=30')
fs.createWriteStream(filepath).pipe(res)
}
协商缓存中header的参数(响应头)
- Last-Modified:资源的最后修改时间。
- Etag:资源的唯一标识(一个字符串,类似于人类的指纹)。
服务端拿到 if-Modified-Since 之后拿这个时间去和服务端资源最后修改时间做比较,如果一致则返回 304 ,不一致(也就是资源已经更新了)就返回 200 和新的资源及新的 Last-Modified。
其实 Etag 和 Last-Modified 一样的,只不过 Etag 是服务端对资源按照一定方式(比如 contenthash)计算出来的唯一标识,就像人类指纹一样,传给客户端之后,客户端再传过来时候,服务端会将其与现在的资源计算出来的唯一标识做比较,一致则返回 304,不一致就返回 200 和新的资源及新的 Etag。
两者比较
- 优先使用 Etag。
- Last-Modified 只能精确到秒级。
- 如果资源被重复生成,而内容不变,则 Etag 更精确。
const http = require('http') const url = require('url') const path = require('path') const { stat, readFile } = require('fs/promises') const crypto = require('crypto') // stat 读取路径 http .createServer(async (req, res) => { let { pathname } = url.parse(req.url) // console.log(pathname) // console.log(path.join(__dirname, '../', pathname, 'static/index.html')) let fileName = pathname === '/' ? path.join(__dirname, '../', pathname, 'static/index.html') : path.join(__dirname, '../', pathname) // console.log(fileName) try { const statObj = await stat(fileName) console.log(statObj.ctime) // 协商缓存 /** * Last-Modified/If-Modified-Since和Etag/If-None-Match,其中Etag/If-None-Match的优先级比Last-Modified / If-Modified-Since高。 */ /* atime 创建时间 mtime 修改时间 ctime 任何东西 */ const ctime = statObj.ctime.toGMTString() res.setHeader('Last-Modified', ctime) // 根据秒的方式 if (req.headers['if-modified-since'] === ctime) { return (res.statusCode = 304) && res.end() }