大白话聊聊前端网络缓存(强缓存、协商缓存)

240 阅读6分钟

先叠个甲

本文章是作者学习前端网络部分知识的一个记录,所有的知识点不保证100%准确。若有不准确的地方,请您及时指正,感谢!

本文章会向你介绍前端缓存(http缓存),以及缓存的使用场景。作者希望用简短的语言介绍该知识点,如果你不喜欢长篇大论的文章,那看这一篇就够了。

啥是缓存?

缓存是一种临时存储机制,能够将第一次获取的内容保存在本地(内存或硬盘中),以便在后续访问时直接从本地读取,而无需重新请求服务器。这样既可以提升请求速度、降低服务器的负载,又减少不必要的网络传输。

啥是HTTP缓存?

http缓存分为强缓存和协商缓存。

简单来说,强缓存就是第一次请求成功后,服务器响应了一个过期时间,在这个过期时间之内,下次请求该地址时,浏览器无需请求服务器,直接从本地读取内容并响应。而协商缓存就是第一次请求成功后,服务器返回内容后,每次请求还要向服务器询问是否可以使用缓存,如果得到允许,才能使用本地缓存的内容。

下面详细介绍一下这两种缓存策略

强缓存

通过Cache-Control实现强缓存

假设服务器想对https://www.image.com/abc进行强缓存,服务器会设置它的响应头为

'Cache-Control':'max-age=100'

当浏览器访问该url,并拿到响应结果后,看到这段响应头,浏览器就会把响应的内容缓存下来,往后100秒内,再访问此url地址,浏览器不会向服务器交互,而是直接从本地获取内容,并响应。

Cache-Control有多个属性,我挑几个重要的属性讲解。

max-age

资源缓存的时间,例如'Cache-Control':'max-age=1000',代表该资源从服务器响应后,浏览器缓存1000秒,这1000秒内,浏览器会使用本地缓存。超过1000秒,进行协商缓存。(协商缓存下面会介绍)

no-cache

强制进行协商缓存。例如'Cache-Control':'no-cache',代表该资源从服务器响应后,不进行强缓存而是协商缓存。

no-store

不进行任何缓存

这里要注意,no-cache虽然译为无缓存或不缓存,但在Cache-Control中他的意思是协商缓存。no-store才是无缓存或不缓存的意思,这里千万不要搞混!

通过Expires实现强缓存

Expires是HTTP/1.0的字段,他已经被Cache-Control替代,这里简单讲一下。

假设服务器想对https://www.image.com/abc进行缓存,并使用Expries方案,服务器会设置它的响应头为

Expires: Wed, 21 Oct 2025 07:28:00 GMT

他的意思是:浏览器在 2025 年 10 月 21 日 07:28:00 之前可以直接使用缓存,而无需再次请求服务器。超过该时间后,浏览器会重新请求服务器以获取更新内容。

Expires的时间被Cache-Control替代的原因是,Expires返回的是一个具体的时间,而Cache-Control返回的是一个倒计时(单位是秒),这里有个问题,假设本地的时间不准或有变更,Expires缓存的时间可能会变长或变短,甚至永久缓存。

为了向下兼容,有时会看到服务器同时设置 ExpiresCache-Control,例如:

Cache-Control: max-age=600
Expires: Wed, 21 Oct 2025 07:28:00 GMT

这种情况下,Cache-Controlmax-age 优先级更高。如果 max-ageExpires 的值不同,浏览器会优先使用 Cache-Control: max-age 的时间。

协商缓存

假设服务器想对https://www.image.com/abc进行协商缓存,服务器会设置它的响应头为

Cache-Control: no-cache 
ETag: "abc123" // 唯一标识符 
Last-Modified: Wed, 01 Jan 2024 00:00:00 GMT // 最后修改时间

ETagLast-Modified是常用的两个响应头,它们用于标识资源的变化。

这里简单介绍一下ETagLast-Modified

ETag是资源生成的hash值(文件指纹),如果文件内容为 Hello, World!,可以计算其 MD5 哈希值(例如 65a8e27d8879283831b664bd8b7f0ad4),并作为 ETag

Last-Modified是资源文件上一次的修改时间

当浏览器在响应成功后,在响应头读取到ETagLast-Modified,并且Cache-Control: no-cache ,会将资源缓存,并在下次的请求头中携带字段:

If-None-Match: "abc123" // 使用上次获取的ETag 
If-Modified-Since: Wed, 01 Jan 2024 00:00:00 GMT // 使用上次获取的Last-Modified

服务器验证时间与文件指纹正确后,就会返回304状态码,允许浏览器使用本地缓存。

读到这大家有没有看懂协商缓存与强缓存的区别,他们的区别就是强缓存第一次得到响应后,在特定的时间(max-age未过期)内,自动使用本地缓存。而协商缓存需要每一次都向服务器验证缓存的资源是否有效,得到服务器的验证后才会使用缓存。

注意:有时候服务器不一定同时返回ETagLast-Modified,因为ETag需要将文件内容进行hash编码,这个过程会给服务器造成压力。假设服务器没有返回ETag,浏览器在下次请求时,也就不会在响应头携带If-None-Match

Last-Modified同理。

强缓存与协商缓存同时存在的情况

还记得Cache-Controlmax-age属性吗?我最后写了一句话:“超过1000秒,进行协商缓存”。

这时可能会有疑惑,max-age不是强缓存吗?

这里我想说一下,max-age也可以做协商缓存。下面我来举个例子。

假设你第一次请求某个资源,服务器可能会设置响应头为:

Cache-Control: max-age=1000
ETag: "abc123"
Last-Modified: Wed, 01 Jan 2024 00:00:00 GMT

这代表在1000秒之内,服务器默认使用强缓存。在1000秒内请求该地址,浏览器会直接使用本地资源。超过1000秒之后,浏览器会带上 If-None-MatchIf-Modified-Since 进行协商缓存验证。服务器如果验证通过并返回 304 状态码,则继续使用本地缓存;否则,返回新的内容。

用人话说就是,浏览器在1000秒之内使用强缓存,超过1000秒之后,浏览器不知道该资源是否过期,它还不想直接放弃这个资源的本地缓存,所以会带上 If-None-MatchIf-Modified-Since去进行协商缓存验证。

写在最后

感谢您能看完,如有任何错误,请在评论区或私信联系我,我会立刻改正!