YBFACC / blog

仅记录个人学习使用
3 stars 0 forks source link

协议缓存 #13

Open YBFACC opened 4 years ago

YBFACC commented 4 years ago

协议缓存

我的完整代码

与强缓存的对比

强缓存:状态码是200,服务器并不会收到请求,浏览器直接使用缓存。

协议缓存:服务器收到请求并进行比较处理,如果文件没有改变就返回状态码 304,否则加上 Haeder 标记、状态码 200 、并重新返回资源。

强缓存的优先级高于协议缓存

Header 一览

请求头 if-modified-since 和响应头 last-modified

The Last-Modified是一个响应首部,其中包含源头服务器认定的资源做出修改的日期及时间。 它通常被用作一个验证器来判断接收到的或者存储的资源是否彼此一致。由于精确度比 ETag 要低,所以这是一个备用机制。包含有 If-Modified-SinceIf-Unmodified-Since 首部的条件请求会使用这个字段。

语法

Last-Modified: <day-name>, <day> <month> <year> <hour>:<minute>:<second> GMT

引用MDN

缺陷

请求头 if-none-match 和响应头 etag

ETagHTTP响应头是资源的特定版本的标识符。这可以让缓存更高效,并节省带宽,因为如果内容没有改变,Web服务器不需要发送完整的响应。而如果内容发生了变化,使用ETag有助于防止资源的同时更新相互覆盖(“空中碰撞”)。

语法

ETag: W/"<etag_value>"
ETag: "<etag_value>"

引用MDN

测试

原生使用if-modified-since 和 last-modified

  1. 以防万一我们先关闭强缓存 Cache-control:no-cache。
  2. 我们获取 Header 中的 if-modified-since
  3. 通过 fs 模块获取文件最后一次修改时间。
  4. if-modified-since 与最后一次时间进行比较。如果没有改变就返回 304 ,如果不一致则返回 200 并加上 last-modified 告诉浏览器这个文件最后一次修改时间。
ctx.res.setHeader('Cache-control', 'no-cache')

const ifModifiedSince = ctx.req.headers['if-modified-since']

let stats = fs.statSync('./cache/png.png')

let change_time = stats.mtime.toUTCString()

if (change_time === ifModifiedSince) {
  ctx.res.writeHead(304)
} else {
  ctx.res.setHeader('last-modified', change_time)
  ctx.type = 'image/png'
  ctx.res.writeHead(200)
  let img = fs.readFileSync('./cache/png.png')
  ctx.res.write(img, 'binary')
  ctx.res.end()
}

原生使用请求头 if-none-match 和响应头 etag

与上次的步骤基本一致。

  1. 强校验不一样的就是通过 crypto 模块获得文件的 md5 值,每次比较的是 md5 值而不是时间。
ctx.res.setHeader('Cache-control', 'no-cache')

const ifNoneMatch = ctx.req.headers['if-none-match']

const hash = crypto.createHash('md5')

let css = fs.readFileSync('./cache/test.css', 'utf-8')
hash.update(css)
const etag = `"${hash.digest('hex')}"`

if (ifNoneMatch === etag) {
  ctx.res.writeHead(304)
} else {
  ctx.res.setHeader('etag', etag)
  ctx.type = 'text/css'
  ctx.res.writeHead(200)
  ctx.res.write(css, 'binary')
  ctx.res.end()
}
  1. 弱检验:根据文件的最后修改时间和文件大小计算合并得到。(参考express里的实现)
ctx.res.setHeader('Cache-control', 'no-cache')

const ifNoneMatch = ctx.req.headers['if-none-match']

let stats = fs.statSync('./cache/png1.png')

var mtime = stats.mtime.getTime().toString(16)
var size = stats.size.toString(16)

let change_time = 'W/' + `"${size}-${mtime}"`

if (change_time === ifNoneMatch) {
  ctx.res.writeHead(304)
} else {
  ctx.res.setHeader('etag', change_time)
  ctx.type = 'image/png'
  ctx.res.writeHead(200)
  let img = fs.readFileSync('./cache/png1.png')
  ctx.res.write(img, 'binary')
  ctx.res.end()
}

使用协议缓存还是强缓存?

用户行为 强缓存 协议缓存
普通刷新 Yes Yes
强制刷新 No No
前进后退 ?? ??
页面跳转 Yes Yes
新标签页输入网址 Yes Yes

前进后退的行为

参考

一文读懂前端缓存

MDN

通过 koa2 服务器实践探究浏览器HTTP缓存机制