HTTP缓存,我来带你做!

71 阅读3分钟

HTTP缓存是什么

HTTP缓存是当客户端(代理服务器)首次接收到服务端响应资源后,通过服务端响应的返回标识,存储到客户端或者是代理服务器本地的资源副本

当后续再次发起请求的时候,客户端会首先拦截请求查看是否请求命中缓存,如果命中缓存,则使用缓存资源加载。这样就有效减轻服务器的压力,加速应用的页面渲染

HTTP缓存有什么?

主要分为以下缓存方式:

  1. 强缓存 image.png

  2. 协商缓存 image.png

实现缓存,我带你做!

  1. 创建项目
  2. 编写index.html
  3. 使用nodejs搭建http服务器
  4. 编写访问index.html
  5. 编写强缓存-Expires
  6. 编写强缓存-Cache-Control
  7. 编写协商缓存- Last-Modified
  8. 编写协商缓存- Etag

新建一个项目

image.png

images目录 --- 模拟服务器存储图片的目录

server目录 --- nodejs服务端代码

index.html --- 主文件

编写index.html文件

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>HTTP缓存</title>
  </head>
  <style>
    img {
      width: 100px;
    }
  </style>
  <body>
    <h1>强缓存-Expires</h1>
    <p>
      <img src="/images/01.webp" alt="01.webp" />
    </p>
    <h1>强缓存缓存-Cache-Control-Max-Age</h1>
    <p>
      <img src="/images/02.webp" alt="" />
    </p>
    <h1>协商缓存-Last-Modified</h1>
    <p>
      <img src="/images/03.webp" alt="" />
    </p>
    <h1>协商缓存-Etag</h1>
    <p>
      <img src="/images/04.webp" alt="" />
    </p>
  </body>
</html>

搭建nodejs服务

const http = require("http");
const url = require("url");
const fs = require("fs");
const utils = require("util");
const etag = require("etag");
const { promisify } = utils;

const readFilePromisify = promisify(fs.readFile);

http
  .createServer(async (req, res) => {
      res.end('port opened in 3000')
  }).listen(3000)

编写访问index.html文件的代码

// 在http.createServer的回调函数中添加以下代码
if (pathname === "/") {
      const htmlContent = await readFilePromisify("../index.html");
      res.end(htmlContent);
} else {
      res.statusCode = 404;
      res.end();
}

借此,服务端可以读取到index.html的文件流并返回到客户端,展示基本的页面(index.html)

此时根据浏览器渲染,浏览器会解析HTML,当解析到img标签的 src 属性后,会去加载对应链接的图片资源

此时当浏览器接收到请求后,我们为响应的资源设置缓存

强缓存-Expires

// ... if(pathname==='/')
else if (pathname === "/images/01.webp") {
      // 强缓存-Expires,注意在chrome的控制台不需要打开 disabeled cache,设置过期时间,时间依赖本地计算机时间
      const img1Buffer = await readFilePromisify("../images/01.webp");
      res.writeHead(200, {
        Expires: new Date(`2022-7-12 10:35:59`).toUTCString(),  // 时间可以设置当前时间往后推1-2分钟
      });

      res.end(img1Buffer);
    }
// ...

第一次请求: image.png

第二次请求(使用强缓存,并且服务端没有接收到对应请求,可以通过在服务端打印req.url的pathname查看): image.png

当过期时间已失效,则Expires强缓存失效,每次请求都会获取最新资源

image.png

强缓存-Cache-Control

else if (pathname === "/images/02.webp") {
      // 强缓存-cache-control,此时cache control需要设置max-age 用以兼容之前的expires,单位为秒,设置过期时长
      const img2Buffer = await readFilePromisify("../images/02.webp");

      res.writeHead(200, {
        "Cache-Control": "max-age=10",
      });

      res.end(img2Buffer);
}

第一次请求: image.png

第二次请求(10s之内,使用强缓存,并且服务端没有接收到对应请求): image.png

10s之后的第三次请求,得到的结果为 第一次请求的结果

协商缓存- Last-Modified

 else if (pathname === "/images/03.webp") {
      // 协商缓存 Last-Modified(服务端) 和 客户端(If-Modified-Since)
      const { mtime } = fs.statSync("../images/03.webp"); // 获取文件属性状态中的修改时间mtime(Date类型)

      if (req.headers["if-modified-since"] === mtime.toUTCString()) {
        res.statusCode = 304;
        res.end();
      } else {
        const img4Buffer = await readFilePromisify("../images/03.webp");

        res.writeHead(200, {
          "Last-Modified": mtime.toUTCString(),
          "Cache-Control": "no-cache", // 用以告知客户端,下次请求强缓存失效后需要协商缓存
        });

        res.end(img4Buffer);
      }
    }

第一次请求: image.png

后续请求(资源Last-Modified未修改的情况下) image.png

协商缓存 - Etag

else if (pathname === "/images/05.webp") {
      const img5Buffer = await readFilePromisify("../images/05.webp");
      const etagContent = etag(img5Buffer); // 根据读取到的文件二进制流生成etag指纹签名

      if (req.headers["if-none-match"] === etagContent) {
        res.statusCode = 304;
        res.end();
      } else {
        res.writeHead(200, {
          'Etag': etagContent,
          "Cache-Control": "no-cache", // 用以告知客户端,下次请求强缓存失效后需要协商缓存
        });
        res.end(img5Buffer);
      }
    }

第一次请求: image.png 后续请求(资源内容未修改的情况下) image.png

源码地址

github.com/henrychinaw…