HTTP缓存是什么
HTTP缓存是当客户端(代理服务器)首次接收到服务端响应资源后,通过服务端响应的返回标识,存储到客户端或者是代理服务器本地的资源副本
当后续再次发起请求的时候,客户端会首先拦截请求查看是否请求命中缓存,如果命中缓存,则使用缓存资源加载。这样就有效减轻服务器的压力,加速应用的页面渲染
HTTP缓存有什么?
主要分为以下缓存方式:
-
强缓存
-
协商缓存
实现缓存,我带你做!
- 创建项目
- 编写index.html
- 使用nodejs搭建http服务器
- 编写访问index.html
- 编写强缓存-Expires
- 编写强缓存-Cache-Control
- 编写协商缓存- Last-Modified
- 编写协商缓存- Etag
新建一个项目
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);
}
// ...
第一次请求:
第二次请求(使用强缓存,并且服务端没有接收到对应请求,可以通过在服务端打印req.url的pathname查看):
当过期时间已失效,则Expires强缓存失效,每次请求都会获取最新资源
强缓存-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);
}
第一次请求:
第二次请求(10s之内,使用强缓存,并且服务端没有接收到对应请求):
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);
}
}
第一次请求:
后续请求(资源Last-Modified未修改的情况下)
协商缓存 - 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);
}
}
第一次请求:
后续请求(资源内容未修改的情况下)