前言

HTTP的缓存控制 可以说是前端工程师需要掌握的重要的知识之一 本文将针对HTTP缓存整体流程来做一个详细的讲解
HTTP缓存分为两种 一种是强缓存 一种是协商缓存 主要作用是可以加快资源的获取速度,提高用户体验 同时减少服务器的压力 这是缓存的整体流程图
图解http缓存 - 图1

强缓存

Expires http1.0

Expires 的值是一个http的日期,在浏览器发起请求的时候, 会根据系统时间和Expires的值就行比较 如果系统的时间超过了Expires的值,缓存失效,由于和系统时间进行比较,所以当系统时间和服务器的时间不一致的问题,就会出现有限期不准的问题。 所以Expires的优先级是最低的。
**

Cache-Control http1.1

Cache-Control 是HTTP1.1 中新增的属性。 在请求头和响应头中使用。 常用的属性值有

  • max-age: 单位是秒 这是个相对时间,那么这个max-age是根据谁来说的呢,我们get到资源的时候响应头以一个Date字段,就是根据Date的来确定的 。 如果发起请求的时候超过了这个时间,那么缓存失效
  • no-cache: 不使用强缓存, 需要和服务器验证缓存时候新鲜
  • no-store: 禁用缓存
  • private: 专用个人缓存。中间代理 CND等不能缓存次响应
  • public: 响应可以被中间代理 CDN等缓存
  • mast-revalidate: 缓存过期前可以使用 过期后必须向浏览器验证

    Pragma

    Pragma只有一个属性值 就是no-cache ,效果上和Cache-Control中的no-cache一致, 不使用强缓存,需要与服务器验证缓存是否新鲜,在3个头部属性中的优先级最高

本地通过express起一个服务器来验证强缓存的3个属性

  1. const express = require('express');
  2. const app = express();
  3. var options = {
  4. etag: false, // 禁用协商缓存
  5. lastModified: false, // 禁用协商缓存
  6. setHeaders: (res, path, stat) => {
  7. res.set('Cache-Control', 'max-age=10'); // 强缓存超时时间为10秒
  8. },
  9. };
  10. app.use(express.static((__dirname + '/public'), options));
  11. app.listen(3000);

第一次加载 页面向服务器请求数据 并在Response Header里添加Cache-Control 过期时间为10s
图解http缓存 - 图2

在10s内,第二次加载Date 头属性并未更新,可以看到浏览器直接使用了强缓存, 实际请求并没有发出
图解http缓存 - 图3

过了十秒,第三次请求
图解http缓存 - 图4

当Pragma和Cache-Control同时存在时候, Pragma的优先级是高于Cahce-Control的
**

协商缓存

当浏览器的强缓存失效 或者是在请求头中设置了不走强缓存, 并且子请求头中射中了if-Modified-Since 或者 If-None-Match的时候, 会将这两个属性值到服务端去验证是否命中协商缓存, 如果命中了协商缓存, 那么会返回304, 加载浏览器缓存,并且响应头设置Last-Modified 或 Etag属性

ETag / If-None-Match

Etag/If-None-Match的值是一串hash码,代表的是一个资源的标识符,当服务端的文件变化的时候,他的hash码会随之改变,通过请求头中的If-None-Match和当前文件的hash值进行比较 如果相等 则表示命中协商缓存.
Etag又有强弱之分,如果hash码是以 “W/”开头的一串字符串,说明此时协商缓存的校验是弱校验,只有服务器上的文件差异(根据etag的计算方式来决定)达到能够出发后缀变化的时候才会真正请求资源,否则返回304并假爱浏览器缓存。

Last-Modified / If-Modified-Since

Last-Modified/If-Modified-Since 的值表示文件的最后修改时间,第一次请求服务端会把资源的最后修改时间放到last-modified中去, 第二次放松请求,请求头会带上上一次响应头的last-modified,并放进if-modified-since中去, 服务器根据文件的最后修改时间和if-modified-since比较,如果相等 返回304 并加载浏览器缓存

本地通过express起一个服务来验证协商缓存 ,

  1. const express = require('express');
  2. const app = express();
  3. var options = {
  4. etag: true, // 开启协商缓存
  5. lastModified: true, // 开启协商缓存
  6. setHeaders: (res, path, stat) => {
  7. res.set({
  8. 'Cache-Control': 'max-age=00', // 浏览器不走强缓存
  9. 'Pragma': 'no-cache', // 浏览器不走强缓存
  10. });
  11. },
  12. };
  13. app.use(express.static((__dirname + '/public'), options));
  14. app.listen(3001);

第一次请求资源
图解http缓存 - 图5

第二次请求资源
图解http缓存 - 图6

Etag 和 Last-Modified

ETag/If-None-Match 的出现主要解决了 Last-Modified/If-Modified-Since 所解决不了的问题:

  • 如果文件的修改频率在秒级以下,Last-Modified/If-Modified-Since 会错误地返回 304
  • 如果文件被修改了,但是内容没有任何变化的时候,Last-Modified/If-Modified-Since 会错误地返回 304 ,上面的例子就说明了这个问题