用Koa研究下浏览器缓存策略

1.为什么浏览器会有缓存?

使用浏览器缓存之后,除第一次访问需要向服务器请求网站全部资源,后续访问可以重复使用浏览器缓存,减少对服务器的请求,降低服务器的压力。

2.资源请求流程图

用Koa研究下浏览器缓存策略

3.代码验证(强缓存)

  • http1.0 强制缓存 Expires
const Koa = require('koa')
const Router = require('@koa/router')
const fs = require('fs')
const app = new Koa()
const router = new Router()

router.get('/style.css', (ctx) => {
  console.log('请求了style.css');
  ctx.set('Expires', new Date(Date.now() + 10 * 1000).toUTCString() ) // 缓存10秒
  ctx.body = `
    body {
      background: green;
    }
  `
})

router.get('/index.html', (ctx) => {
  // ****当前访问页不会被强缓存。只有在当前访问页内请求资源,这些资源才可设置缓存。****
  ctx.body = `
  <html>
      <title>浏览器http1.0强制缓存</title>
      <link rel="stylesheet" href="./style.css"></link>
  </html>
  `;
})

app.use(router.routes()).use(router.allowedMethods());

app.listen(4399, () => {
  console.log('service run success');
})
    

打开浏览器NetWork观察

第一次请求style.css,响应头携带Expires缓存时间返回给客户端。
用Koa研究下浏览器缓存策略

再次请求style.css,浏览器会比对客户端时间和缓存时间,如果客户端时间在缓存时间内,则说明命中缓存。直接返回缓存资源。(这里的时间对比可能有问题,服务器时间和客户端时间不同步的问题)
用Koa研究下浏览器缓存策略

过10s后再次请求style.css,缓存失效,请求服务器。
用Koa研究下浏览器缓存策略

服务端打印

用Koa研究下浏览器缓存策略

  • http1.1 强制缓存Cache-Control MDN
const Koa = require('koa')
const Router = require('@koa/router')
const fs = require('fs')
const app = new Koa()
const router = new Router()

router.get('/style.css', (ctx) => {
  console.log('请求了style.css');
  
  // 缓存资源在10秒后过期
  ctx.set('Cache-Control', 'max-age=10') 
  ctx.body = `
    body {
      background: green;
    }
  `
})

router.get('/index.html', (ctx) => {
  // ****当前访问页不会被强缓存。只有在当前访问页内请求资源,这些资源才可设置缓存。****
  ctx.body = `
  <html>
      <title>浏览器http1.1强制缓存</title>
      <link rel="stylesheet" href="./style.css"></link>
  </html>
  `;
})

app.use(router.routes()).use(router.allowedMethods());

app.listen(4399, () => {
  console.log('service run success');
})
    

打开浏览器NetWork观察

第一次请求style.css,响应头携带Cache-Control属性返回

用Koa研究下浏览器缓存策略

再次请求style.css,浏览器会比对客户端和缓存的相对时间,在缓存相对时间内则说明命中缓存。直接返回缓存资源。
用Koa研究下浏览器缓存策略

过10s后再次请求style.css,缓存失效,请求服务器。
用Koa研究下浏览器缓存策略

服务端打印
用Koa研究下浏览器缓存策略

强制缓存总结

  • http1.0 服务端设置响应头Expries字段,来设置绝对过期时间。服务器时间和客户端时间不一致问题,导致缓存失效。
  • http1.1 服务端设置响应头Cache-Control字段,来设置相对过期时间。都在客户端这边做时间比对,解决了http1.0 Expires 时间不一致的问题。
  • Cache-ControlExpries可以一起在响应头返回。Cache-ControlExpries优先级高。

4.代码验证(协商缓存)

  • http1.0 强制缓存 Last-Modified/if-modified-sence
const Koa = require('koa')
const Router = require('@koa/router')
const fs = require('fs')
const app = new Koa()
const router = new Router()

// http1.0 通过资源修改时间来判断是否采用缓存(只能精确到秒) 是不精准的。
// 资源文件在1秒内修改多次,缓存会失效。
router.get('/style.css', (ctx) => {

  ctx.set('Cache-Control', 'no-cache')
  const ifModifiedSince = ctx.get('if-modified-since')
  const stat = fs.statSync('./style.css')
  const ctime = stat.ctime.toUTCString()
  if(ctime === ifModifiedSince) {
    ctx.status = 304
    return
  }
  ctx.set('Last-Modified', ctime)
  ctx.body = fs.createReadStream('./style.css')
})

router.get('/index.html', (ctx) => {
  // ****当前访问页不会被强缓存。只有在当前访问页内请求资源,这些资源才可设置缓存。****
  ctx.body = `
  <html>
      <title>浏览器http1.0强制缓存</title>
      <link rel="stylesheet" href="./style.css"></link>
  </html>
  `;
})

app.use(router.routes()).use(router.allowedMethods());

app.listen(4399, () => {
  console.log('service run success');
})
    

打开浏览器NetWork观察

第一次请求style.css,响应头携带Last-Modified缓存时间返回给客户端。
用Koa研究下浏览器缓存策略

再次请求style.css,客户端会把上次Last-Modified时间放在请求头if-modified-sence上。
用Koa研究下浏览器缓存策略

服务端接收到客户端请求后,对比请求头携带的文件修改时间ifModifiedSince和当前文件最后修改时间ctime做对比。如果相同就返回304状态码,响应体返回空。

    const ifModifiedSince = ctx.get('if-modified-since')
    const stat = fs.statSync('./style.css')
    const ctime = stat.ctime.toUTCString()
    
    if(ctime === ifModifiedSince) {
       ctx.status = 304
       return
    }

用Koa研究下浏览器缓存策略

  • http1.1 强制缓存 Etag/if-none-match
const Koa = require('koa')
const Router = require('@koa/router')
const fs = require('fs')
const app = new Koa()
const router = new Router()
const crypto = require('crypto')

const md5 = (content) => crypto.createHash('md5').update(content).digest('base64')

// http1.1 md5 
// 缺点 如果把资源都摘要一遍 性能不太好。 
// 解决 一般不会摘要其全部类容,只摘要部分类容 + 文件大小
router.get('/style.css', (ctx) => {
  const ifNoneMatch = ctx.get('if-none-match')
  const fileContent = fs.readFileSync('./style.css')
  const etag = md5(fileContent)
  if(ifNoneMatch === etag) {
    ctx.status = 304
    return 
  }
  ctx.set('Etag', etag)
  ctx.body = fileContent
})

router.get('/index.html', (ctx) => {
  // ****当前访问页不会被强缓存。只有在当前访问页内请求资源,这些资源才可设置缓存。****
  ctx.body = `
  <html>
      <title>浏览器http1.1强制缓存</title>
      <link rel="stylesheet" href="./style.css"></link>
  </html>
  `;
})

app.use(router.routes()).use(router.allowedMethods());

app.listen(4399, () => {
  console.log('service run success');
})
    

打开浏览器NetWork观察

第一次请求style.css,响应头携带Etag``md5摘要返回给客户端。
用Koa研究下浏览器缓存策略

再次请求style.css,客户端会把上次Etag摘要放在请求头if-no-match上。
用Koa研究下浏览器缓存策略

服务端接收到客户端请求后,对比请求头携带的md5摘要ifNoneMatch和当前文件摘要etag做对比。如果相同就返回304状态码,响应体返回空。

  const ifNoneMatch = ctx.get('if-none-match')
  const fileContent = fs.readFileSync('./style.css')
  const etag = md5(fileContent)
  if(ifNoneMatch === etag) {
    ctx.status = 304
    return 
  }

用Koa研究下浏览器缓存策略

协商缓存总结

  • http1.0 基于Last-Modifiedif-modified-sence来对比文件最后修改时间。如果两者相同,就返回304状态码,响应体返回空。缺点:文件修改时间单位是秒,如果在1秒内修改多次,缓存就会失效。
  • http1.1 基于Etagif-none-match来对比md5摘要,如果两者相同,就返回304状态码,响应体返回空。缺点:如果摘要全部资源,性能不太好。一般不会摘要全部,只摘要部分内容 + 文件大小。

原文链接:https://juejin.cn/post/7214318587431141434 作者:ty66

(0)
上一篇 2023年3月25日 下午7:54
下一篇 2023年3月25日 下午8:05

相关推荐

发表回复

登录后才能评论