http缓存

http缓存

http缓存分为强缓存和协商缓存

强缓存:在访问某个资源时,浏览器中有它的缓存副本,且该副本有效,那么浏览器可以直接从缓存中获取资源,不用在向服务器发起请求

协商缓存:在访问某个资源时,浏览器中有它的缓存副本,但是该缓存已经失效,那么浏览器就需要向服务器发起http请求,询问缓存是否可以继续使用。若该资源没有变化缓存可以用,服务器就返回304状态码告知浏览器;若资源发生了变化,则会返回新的资源,状态码为200

 

强缓存相关字段

1.expires

  该字段是一个响应字段,它的值是一个时间戳,代表在此之前缓存是有效的,过了这个时间之后缓存便会失效。但是若服务器的时间和本地的时间有差别,或者修改了本地时间,有可能会导致缓存失效

2.cache-control

  由于expires的不足,在HTTP1.1中定义了cache-control字段,优先级高于expires。这是一个通用字段,在请求或响应中都能使用。
  请求指令:
    1)no-cache  强制向服务器再次验证
    2)no-store    不缓存
    3)max-age   响应的最大值
  响应指令:
    1)no-cache  缓存,但必须向服务器确认其新鲜度
    2)no-store    不缓存
    3)max-age   响应的最大值,缓存的有效时长
    4)private      只有客户端可缓存响应
    5.public       任意方都可缓存响应

具体内容可见《图解HTTP》

协商缓存相关字段

1.Last-Modified

  响应字段,值代表最后修改时间。浏览器第一次向服务器请求资源时,服务器会在响应头中携带该字段,代表资源最后的修改时间

2.If-Modified-Since

  请求字段,当缓存失效后,浏览器再次请求该资源时,会向服务器发送一次请求,检查缓存的新鲜度。请求头中会携带该字段,值为Last-Modified的值。与服务器资源的Last-Modified进行比较。若不相等则代表资源更新了,返回新的资源;若相等则代表资源没有更新,返回304状态码,告诉浏览器缓存可以用。

  不过该字段存在缺点,若修改服务端资源的过程中,时长不超过1秒,则Last-Modified的值不会发生变化,也就不会检测到资源的更新

3.ETag

  响应字段,浏览器第一次向服务器请求资源时,服务器会在响应头中携带该字段,它的值是服务器根据要请求的资源用算法生成的,资源发生变化时其值也跟着发生变化

4.If-None-Match

  请求字段,同样当缓存失效后,浏览器会在请求头中携带该字段,值为ETag的值,与服务器资源的ETag进行比较。若不相等则代表资源更新了,返回新的资源;若相等则代表资源没有更新,返回304状态码,告诉浏览器缓存可以用。该字段的优先级高于Last-Modified

简单模拟实现强缓存和协商缓存

新建server.js,用node开启一个服务器,监听3000端口

var http = require('http')
var fs = require('fs')

var server = http.createServer()

server.on('request', function (req, res) {
 // 设置cors跨域 res.setHeader(
'Access-Control-Allow-Origin', '*')
 // 设置expires,代表这个时间点之后缓存会失效 res.setHeader(
'expires', 'Wed, 07 Oct 2020 05:25:44 GMT')
 // 设置Last-Modified,文件最后修改时间(这里只是简单的模拟,所以给赋予一个固定的值) res.setHeader(
'Last-Modified', 'Wed, 07 Oct 2020 04:59:26 GMT')
 if (req.headers['if-modified-since'] === 'Wed, 07 Oct 2020 04:59:26 GMT') { res.statusCode = 304 res.end() } else { if (req.url === '/a.html') { fs.readFile('./a.html', 'utf-8', function (err, data) { res.end(data) }) } } }) server.listen(3000, function () { console.log('server is open') })

 

在同一目录下新建a.html

<div>aaaaa</div>


新建index.html,点击按钮触发ajax请求,访问服务器中的a.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
  <button onclick="fn()">aa</button>

  <script>
    function fn () {
      var xhr = new XMLHttpRequest()
      xhr.open('get', 'http://localhost:3000/a.html')
      xhr.onreadystatechange = function () {
        if (xhr.readyState === 4 && xhr.status === 200) {
          console.log(xhr.responseText)
        }
      }
      xhr.send()
    }

  </script>
</body>
</html>


开启服务器后,在本地打开index.html,然后点击按钮发起请求

 

 
可以看到在第二次访问中,expires设置的时间大于Date,说明缓存还没有过期,所以直接访问了缓存中的资源


 
第三次的访问中,缓存已经失效,浏览器向服务器发起的请求中携带了If-Modified-Since字段来检验缓存的新鲜度。由于这里只是简单的模拟,服务端代码中固定了Last-Modified的值,并返回304状态码

在server.js中加上Cache-Control和ETag

var http = require('http')
var fs = require('fs')

var server = http.createServer()

server.on('request', function (req, res) {
  res.setHeader('Access-Control-Allow-Origin', '*')
  res.setHeader('expires', 'Wed, 07 Oct 2020 06:22:44 GMT')
  res.setHeader('Last-Modified', 'Wed, 07 Oct 2020 04:59:26 GMT')
  res.setHeader('ETag', '123456abcdef')
  res.setHeader('Cache-Control', 'max-age=10')  
  if (req.headers['if-none-match'] === '123456abcdef') {
    res.statusCode = 304
    res.end()
  } else {
    if (req.url === '/a.html') {
      fs.readFile('./a.html', 'utf-8', function (err, data) {
        res.end(data)
      })
    }
  }
})

server.listen(3000, function () {
  console.log('server is open')
})


重启服务器(发起请求前先清除浏览器缓存)


 

 
Cache-Control的优先级大于expires,Cache-Control中设置max-age为10,由于第二次访问时10秒还没有过去,所以浏览器访问了缓存中的资源

 

 
第三次访问时,10秒已经过去,缓存失效。浏览器向服务器发起的请求中携带了If-Modified-Since字段和If-None-Match字段来检验缓存的新鲜度,ETag的优先级大于Last-Modified。同样因为是简单模拟,所以ETag的值是随便赋值的字符串,然后服务端返回304状态码







posted on 2020-10-07 15:25  尘光  阅读(261)  评论(0)    收藏  举报

导航