【记一忘三二】浏览器缓存

80 阅读5分钟

浏览器缓存

图片

在同一个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>

image-20240817002442277

只有一次cc.jpg的请求记录

image-20240817002509631

但是动态加载图片的JS代码确实执行了

这就是图片预加载的原理,对于当前Tab进程的所以同一个地址的图片请求,只会执行第一次

但这里需要注意

setTimeout(() => {
    fetch('./cc.jpg').then(res => {
        console.log("cc.jpg is fetched")
    })
}, 1000)

image-20240817002819318

使用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>

image-20240817003115064

但是把延时时间改为0

image-20240817003810103

这里猜测浏览器对于JS文件的缓存持续时间非常短

但是这里还发现,如果服务端设置了强缓存,持续时间又会很久,延时1S之后还是取的缓存

image-20240817010336734

但是协商缓存又会很短,把延时时间改为0才能取缓存,不确定性很大

image-20240817010449586

服务器端缓存

特点

  • 只针对GET请求类型
  • 缓存时一种服务器处理请求的一种策略,也就是必须服务端有缓存的设置
  • 在请求URL加上随机后缀,用于强制不使用缓存策略(强缓存、协商缓存)

用例

image-20240816150003688

// 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

结果

image-20240816163228391

image-20240816163155812

资源文件只有第一次会请求服务器,当服务器设置了强制缓存时间,那么再次发生相同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);

使用的是文件内容的哈希值,在修改文件时,哈希值也会发生改变

结果

image-20240816163228391

image-20240816163455956

响应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是有区别的