HTTP中的Cache-Control头的应用

1,266 阅读3分钟

  在HTTP中,缓存是一个性能优化的关键点。本次来讲解目前使用Cache-Control字段的最佳实践。

强缓存
  第一个场景,在根目录中加载了一个网页,网页中引入一个js脚本资源。对于js脚本的请求,设置Cache-Control中max-age为20,未加入其他参数。服务器代码如下:

const http = require('http')
const fs = require('fs')
http.createServer(function(request, response){
  console.log('request come', request.url)

  if(request.url === '/'){
    const html = fs.readFileSync('test.html', 'utf8')
      response.writeHead(200, {
        'Content-Type': 'text/html'
      })
    response.end(html)
  }

  if(request.url === '/script.js'){
    response.writeHead(200, {
      'Content-Type': 'text/javascript',
      'Cache-Control': 'max-age=20'
    })
    response.end('console.log("script loaded")')
  }
}).listen(8888)

console.log('server listening on 8888')

  所加载的test.html文本内容如下,通过script标签来加载script.js文件,将请求发送到服务器中:

<html>
  <head>
    <title>Document</title>
  </head>
<body>
</body>
<script src="/script.js"></script>
</html>

  网页所加载的script.js文件如下所示,返回的状态码为200,网络传输总大小为205字节,时间为15ms:

  而请求的response中也可以看到Cache-Control字段:

  在20s内第二次请求script.js可以看到网络请求的时长为0ms,大小为memory cached说明,第二次的请求已经走了浏览器缓存,并没有真正发出请求。此时的返回状态码是200:

说明,单独为Cache-Control设置max-age时,属于强缓存,如果没超时,浏览器就直接走缓存。

协商缓存
  如果我们不确定,在max-age这段时间,文档会不会更新,那么我们可以改一种缓存策略,不以时间为规则。想让浏览器先到服务端检查一下文件是否有更新,如果没有更新就走缓存。如果有更新,就重新加载文件。就可以使用Last-Modified和Etag这两个头字段。
  对服务器脚本作如下修改,在Cache-Control中加入no-cache即为协商缓存(覆盖了之前的时间规则,超出时间同样可以协商缓存),此时还需添加两个响应头,Last-Modified为123,Etag为777。如果后续从请求头中取到的if-none-match字段等于777,那么就把状态码改成304,并且返回字符串cached。如果if-none-match不等于777,就正常返回:

const http = require('http')
const fs = require('fs')
http.createServer(function(request, response){
  console.log('request come', request.url)
	
  if(request.url === '/'){
    const html = fs.readFileSync('test.html', 'utf8')
    response.writeHead(200, {
      'Content-Type': 'text/html'
    })
    response.end(html)
  }
  
  if(request.url === '/script.js'){
    const etag = request.headers['if-none-match']
    if(etag === '777'){
      response.writeHead(304, {
        'Content-Type': 'text/javascript',
        'Cache-Control': 'max-age=20, no-cache',
        'Last-Modified': '123',
        'Etag': '777'
      })
      response.end('cached')
    }else{
      response.writeHead(200, {
        'Content-Type': 'text/javascript',
        'Cache-Control': 'max-age=20, no-cache',
        'Last-Modified': '123',
        'Etag': '777'
      })
    }
    response.end('console.log("script loaded")')
  }
}).listen(8888)

console.log('server listening on 8888')

  第一次请求后的返回头可以看到Last-Modified和Etag。

在第二次请求时,可以看到状态码为304:

再看请求的返回内容,服务器返回的‘cached’字符串并没有生效,浏览器还是用之前返回的结果:

最佳实践:

  缓存的意义就在于减少请求,更多地使用本地的资源,尽量命中缓存。那我们每次通过打包软件打完包以后,怎样才能使用户最大限度地利用缓存呢?
  办法就是让用户原来的缓存失效,通过内容hash算法取一个新的名字,再通过入口文件引入。而入口文件,就用协商缓存的方式。

更多精彩文章请关注: