浏览器缓存实践

本文详细探讨了浏览器缓存的实践,包括Cache-Control的各种指令,如max-age、no-store、no-cache,以及Last-Modified、If-Modified-Since、ETag和If-None-Match的工作原理。通过实例和测试脚本,解释了不同缓存策略在刷新、新标签页打开等场景下的应用,并讨论了缓存的优缺点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

缓存实践

在缓存的介绍中,这位大佬的文章已经有非常详细的介绍 浏览器缓存策略之扫盲篇,而且文章还附有相应的代码,但我在阅读的过程中出现一些问题和一些思考,因此就按照大佬的文章再次验证和补充了一些

缓存

有些介绍 HTTP 的书会简单介绍一下缓存,像图解 HTTP 的缓存一章在 5.3 和 6.3 中有相关的介绍,但是都是一些理论知识,实际上看完之后也不知道怎么去用?所以还得去查一下相关的资料来实践一下。

首先我们要知道浏览器下访问资源的方式,下面是歪马大佬写的 浏览器缓存策略之扫盲篇 的介绍内容

浏览器下访问资源的方式主要有以下 7 种:

  1. (新标签)地址栏回车
  2. 链接跳转
  3. 前进、后退
  4. 从收藏栏打开链接(window.open)新开窗口
  5. 刷新(Command + R / F5)
  6. 强制刷新(Command + Shift + R / Ctrl + F5)

使用这 7 种方式访问资源时,应用缓存的策略会有一些不同。如下图所示。通过上述 7 种方式访问资源,会从不同的缓存应用判断步骤开始。此处不做验证,相信大家看了后面的内容,能够自行验证的。
请添加图片描述

需要注意的是,Chrome 中在当前地址栏,不改变内容,直接回车,等同于刷新当前页,而在 Firefox 下与其他在地址栏回车一样。这一点比较特殊,需要适当区分下。

注:本文配有测试脚本,代码在github上。下文会按照测试脚本进行述说,使用说明见下载链接。验证上述内容,可以执行 node cache-ETag+max-age.js,会同时开启 ETag 和 max-age,然后触发相应的动作,通过 Network 面板和 node 日志即可验证,此处篇幅有限先不赘述。

此外,这里提一个概念,webkit 资源分为主资源和派生资源。主资源是地址栏输入的 URL 请求返回的资源,派生资源是主资源中所引用的 JS、CSS、图片等资源。

在 Chrome 下刷新时,只有主资源的缓存应用方式如上图所示,派生资源的缓存应用方式与新标签打开类似,会判断缓存是否过期。强缓存生效时的区别在于新标签打开为 from disk cache,而当前页刷新派生资源是 from memory cache。

而在 Firefox 下,当前页面刷新,所有资源都会如上图所示。下文也会利用 Chrome 的这一特点在当前页刷新,派生资源会使用缓存进行测试。不然每次都需要打开新标签较为繁琐。

Cache-Control:协商缓存

Cache-Control: cache-directive[,cache-directive]

cache-directive 缓存指令,[,cache-directive] 指还可以添加更多指令,比如说 cache-directive,cache-directive…

协商缓存大多用于时间在一定范围内缓存

max-age/Expires

max-age 是以秒(s)来作为计量单位,比如 max-age=60 即缓存时间为 1s。

Expires 有相同功能,但是 max-age > Expires

图文详解可参考,浏览器缓存策略之扫盲篇

注意

使用 github 上的使用样例时,如果出现结果并没有如同文章结果相同,可能是 Chrome 进行了相应的缓存,可以在 F12->Application->Cache->Cache Storage 右键刷新缓存(Refresh Caches),然后再次尝试。

注意

node 使用 github 样例时,如果出现以下错误

// Expires 字段得到了不合法的字符,就是往 Expires 里面写入了不合适的数据
UnhandledPromiseRejectionWarning: TypeError [ERR_INVALID_CHAR]: Invalid character in header content ["Expires"]

可能是 new Date(); // 字符串形式:Sat Oct 30 2021 00:43:32 GMT+0800 (中国标准时间) Expires 无法处理中文,可以直接往 Expires 中填入 Sat Oct 20 2022 00:00:00 GMT+0800 (CST) 来解决这个问题或者使用 new Date().toUTCString()

no-store

请求和响应都可以加上,设置了 Cache-Control: no-store,请求的文件就都不会缓存了,但是浏览器禁用缓存的时候并不是使用的 no-store 而是 no-cache + pragma 的组合

no-cache/Pragma

http1.0 字段, 通常设置为 Pragma:no-cache, 作用与 Cache-Control:no-cache 相同。当在浏览器进行强刷(Comand + Shift + R / Ctrl + F5)或在 NetWork 面板内勾选禁用缓存(Disable Caches)时,会自动带上 Pragma:no-cache 和 Cache-Control:no-cache,并且不会带上协商策略中所涉及的信息(下面介绍的 If-Modified-Since/If-None-Match)。这是不会使用任何缓存,重新获取资源。下面会有相应的图介绍。

Last-Modified/If-Modified-Since

服务器可以设置文件修改时间(比如图片的修改时间)为 Last-Modified,浏览器中会缓存发送过来的 Last-Modified,下次请求(比如刷新)时会携带该值作为 If-Modified-Since 给服务器,而服务器判断该值与当前文件的修改时间是否相同

  1. 如果不同,说明文件已经更新,那就重发
  2. 如果相同,说明文件还没有改变,返回状态码 304,让浏览器使用自己的缓存

具体效果如下
请添加图片描述
第一次请求,服务器发送回来一个红色的照片,服务器响应头会带有 Last-Modified 的值

再次刷新请求一次(ctrl+R)请添加图片描述

状态码是 200 OK (from memory cache),说明图片是从缓存中读取的,它的加载时间也是 0 ms,你可能注意到了

  1. 没有请求头
  2. 也没有按照代码中的那样返回 304
  3. 请求头那里有 Provisional headers are shown. Disable cache to see full headers.

这是因为我们没有走网络请求而是本地服务器(但已经达到了我们缓存的目的),下面是 google 解释的原文

It could be due to the request not sent over the network (served from a local cache), which doesn’t store the original request headers. In this case, you can disable caching to see the full request headers.

如果你想要认真验证的话,也可以去 Firefox 中验证,结果如下图
请添加图片描述

这个图就能够看到浏览器中缓存的 If-Modified-Since 和服务器返回的 304 状态码

到了这里其实还有一个问题,如果我的浏览器是默认不缓存的会怎么样?(比如某些移动端浏览器,用户可以去禁止缓存)发出的请求还会携带 If-Modified-Since 吗?

还是以火狐浏览器(Firefox)为例
请添加图片描述

这个时候无论你刷新多少次,请求头都不会携带 If-Modified-Since,包括下面介绍的 ETag 也不会携带

注:在实际测试中可能会发向浏览器实际效果与文章效果不同,我在测试也遇到了这个问题,比如设置了 Etag,但刷新后请求头就是不带 If-Not-Match,这种情况可以关掉 Chrome 重启,或者使用 Firfox 进行测试

If-Unmodified-Since

从字面上看, 意思是: 如果从某个时间点算起, 文件没有被修改…

  1. 如果没有被修改: 则开始`继续’传送文件: 服务器返回: 200 OK
  2. 如果文件被修改: 则不传输, 服务器返回: 412 Precondition failed (预处理错误)

用途: 断点续传(一般会指定 Range 参数). 要想断点续传, 那么文件就一定不能被修改, 否则就不是同一个文件了, 断续还有啥意义?

我没有找到合适的方法去测试 If-Unmodified-Since

ETag/If-None-Match

由下图可以看到,当我们替换图片的时候,ETag 有了明显的变化
其中 Request Headers 是刷新时,浏览器向服务器中请求的请求头,可以看到当时的 If-None-Match 是 “0dbc…”
请添加图片描述

而服务器这边我们已经修改了图片,所以图片的 md5 已经改变了,所以发送回来的 ETag 也与浏览器先前缓存中的不同,为 “3808…”

服务器中打印的日志也可以验证
请添加图片描述

如果这个时候我们再次刷新,此时服务器响应的 Status Code 会是 304,因为浏览器中已经更新了缓存中的 ETag
请添加图片描述

具体实现可以看源码,非常简单

注:文章中的 ETag 使用 md5 生成

缓存的优缺点

img

参考链接

  1. 浏览器缓存策略之扫盲篇
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值