浏览器缓存
图片
在同一个ab执行进程中,如果再次请求已经请求的url地址,那么就直接会从浏览器缓存中拿,并没有请求记录
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<img src="./cc.jpg">
</body>
<script>
setTimeout(() => {
let imgDom = document.createElement("img")
imgDom.src = "./cc.jpg"
imgDom.onload = function () {
console.log("imgDom onload")
}
document.body.appendChild(imgDom)
}, 1000)
</script>
</html>
只有一次cc.jpg
的请求记录
但是动态加载图片的JS代码确实执行了
这就是图片预加载的原理,对于当前Tab进程的所以同一个地址的图片请求,只会执行第一次
但这里需要注意
setTimeout(() => {
fetch('./cc.jpg').then(res => {
console.log("cc.jpg is fetched")
})
}, 1000)
使用ajax技术的请求并不会使用缓存
JS、CSS
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<script src="./index.js"></script>
<script src="./index.js"></script>
</body>
<script>
setTimeout(() => {
let scriptDom = document.createElement("script")
scriptDom.src = "./index.js"
scriptDom.onload = function () {
console.log("scriptDom onload")
}
document.body.appendChild(scriptDom)
}, 1000)
</script>
</html>
但是把延时时间改为0
这里猜测浏览器对于JS文件的缓存持续时间非常短
但是这里还发现,如果服务端设置了强缓存,持续时间又会很久,延时1S之后还是取的缓存
但是协商缓存又会很短,把延时时间改为0才能取缓存,不确定性很大
服务器端缓存
特点
- 只针对GET请求类型
- 缓存时一种服务器处理请求的一种策略,也就是必须服务端有缓存的设置
- 在请求URL加上随机后缀,用于强制不使用缓存策略(强缓存、协商缓存)
用例
// service.js
const http = require("http");
const url = require("url");
const path = require("path");
const fs = require("fs");
http
.createServer(async (req, res) => {
const { pathname } = url.parse(req.url);
let filePath = path.join(__dirname, "./static", pathname);
if (pathname == "/") {
filePath = path.join(__dirname, "./static/index.html");
}
res.end(fs.readFileSync(filePath));
})
.listen(5000);
强制缓存(浏览器判断)
设置强制缓存,浏览器会先判断请求是否满足使用缓存的条件,如果满足则直接使用浏览器缓存,并不会往服务器发送请求
方案
Expires
res.setHeader("Expires", new Date(Date.now() + 86400000).toGMTString());
使用Expires
是通过服务器的返回时间和浏览器当前时间进行比较,但如果服务器时间和浏览器时间地区不一致,那么缓存比较也就不准确
Cache-Control
res.setHeader("Cache-Control", "max-age=86400");
直接告诉浏览器在多久时间内,使用强制缓存,推荐使用Cache-Control
结果
资源文件只有第一次会请求服务器,当服务器设置了强制缓存时间,那么再次发生相同url的请求时,就会进行比较,判断是否进行强制缓存
注意
- 入口文件(直接请求的HTML文件)设置强制缓存无效
- 当浏览器判断使用强制缓存时,并不会往服务器发送请求
协商缓存(服务器判断)
强制缓存会直接阻止请求到服务器,但是有些资源文件需要动态改动,在改动之后需要浏览器端的请求到最新的,这就是需要浏览器和服务器进行协商,是否使用缓存
在响应浏览器请求时,在响应头上设置文件修改的特征,并且在浏览器端记录,当后续浏览器发生相同请求时,浏览器会带上这条特征信息,然后在进入服务器时,首先进行请求头所带的特征信息和文件修改进行比较,如果相同,则通知浏览器直接走缓存,否则返回文件资源,并且设置最新的文件信息特征
方案
if-modified-since和Last-Modified
// 获取文件最后修改时间
const fileStat = fs.statSync(filePath);
// 判断请求头的协商缓存标识
if (req.headers["if-modified-since"] === fileStat.ctime.toGMTString()) {
res.statusCode = 304;
res.end();
return;
}
// 设置协商缓存响应头部
res.setHeader("Last-Modified", fileStat.ctime.toGMTString());
这里的特征信息是文件的最新的修改时间,但是这里有一个问题,就是这里的时间最小尺度是s,也就是说如果在1s之内发生多次改动,是无法进行更新资源的
Etag和if-none-match
// 获取文件内容的Hash值
const fileContent = fs.readFileSync(filePath);
const fileContentHash = crypto
.createHash("md5")
.update(fileContent)
.digest("base64");
// 判断请求头的协商缓存标识
if (req.headers["if-none-match"] === fileContentHash) {
res.statusCode = 304;
res.end();
return;
}
// 设置协商缓存响应头部
res.setHeader("Etag", fileContentHash);
使用的是文件内容的哈希值,在修改文件时,哈希值也会发生改变
结果
响应Code值是304,并且请求的返回大小和时间有明显减少
请求头中的cache-control
在请求头里面添加cache-control
影响的是当前这一次的请求,响应头里的cache-control
是影响之后的请求
浏览器刷新页面方式
地址栏回车、页面链接跳转、打开新窗口/标签页、history前进后退
从强缓存开始判断
清空缓存重新加载
所有的资源,都会跳过缓存判断,发起真实的请求,从服务端拿资源
这种方式浏览器默认会在请求头里添加Cache-Control:no-cache和Pragma: no-cache
Cache-Control的取值
请求头里Cache-Control只有这两个值可取(max-age=0、no-cache),设置其他的值无效,比如设置Cache-Control:no-store
是没有用的,这一点要和响应头里的Cache-Control
区分开
max-age=0
这请求按照协商缓存的规则走,一定会发出真实的请求
no-cache
这个值一般会附带Pragma: no-cache
,是为了兼容http1.0
表示这次请求不会读缓存资源,即便缓存没有过期,或者资源并没有修改,也就是跳过所有缓存策略,
这样的请求不会有返回304的可能。这一点和响应头里的Cache-Control:no-cache
是有区别的