/技术
分类:技术最近更新:2026-04-15浏览:1467
在开发过程中,经常会提到“缓存”一词,不管是前端还是后端,缓存的主要作用就是缩短请求和相应时间,增强用户体验。在理解前后端缓存之前,我们先查看通常的网络请求:

基本的网络请求就是三个步骤:请求,处理,响应。
在第二步,pedding状态就表示前端在等待服务端处理询问的任务。针对读取的数据,如果有不经常更改的特性,后端经常会把这些数据进行缓存,不去数据库读取,直接从redis或者内存中读取,以实现快速完成响应,缩短响应周期。具体这边不做过多的介绍。可以阅读:https://zhuanlan.zhihu.com/p/32434005
本文主要介绍前端是如何缓存的。上图中,排除第二步,就剩下询问和响应了,分别对应了request和response。
我们在日常请求中会出现下图所示的请求,我们从time请求时长可以看出size为memory cache 和disk cache 请求的时间为比较短,甚至为0。

我们再看请求头,似乎和缓存相关的配置看不到。

再看看响应头消息,我们可以看到Cache-control、date、last-modified、ETag、max-age等信息。

通过整理我们可以得到以下知识点:
memory cache、disk cache、Service Worker、Push Cache 等Cache-Control、ETag 等我们先从缓存位置分类,逐个了解几个类型。
memory cache 是内存中的缓存,(与之相对 disk cache 就是硬盘上的缓存)。按照操作系统的常理:先读内存,再读硬盘。几乎所有的网络请求资源都会被浏览器自动加入到 memory cache 中。但是也正因为数量很大但是浏览器占用的内存不能无限扩大这样两个因素,memory cache 注定只能是个短期存储。常规情况下,浏览器的 TAB 关闭后该次浏览的 memory cache 便告失效 (为了给其他 TAB 腾出位置)。而如果极端情况下 (例如一个页面的缓存就占用了超级多的内存),那可能在 TAB 没关闭之前,排在前面的缓存就已经失效了。
优点:
缺点:
如何触发:
<link rel="preload"> 指定的预加载资源,也会被放入 memory cache在匹配缓存时,除了匹配完全相同的 URL 之外,还会比对他们的类型,CORS 中的域名规则等。因此一个作为脚本 (script) 类型被缓存的资源是不能用在图片 (image) 类型的请求中的,即便他们 src 相等也会重新请求。
在从 memory cache 获取缓存内容时,浏览器会忽视例如 max-age=0、no-cache 等头部配置。例如页面上存在几个相同 src 的图片,即便它们可能被设置为不缓存,但依然会从 memory cache 中读取。这是因为 memory cache 只是短期使用,大部分情况生命周期只有一次浏览而已。而 max-age=0 在语义上普遍被解读为“不要在下次浏览时使用”,所以和 memory cache 并不冲突。也就是说,即便你设置了no-cache,当用户不关闭你的网页窗口,你代码更新,当前页面刷新依旧不读取最新代码,所以这个时候要让客户关闭打开,或者当前页面强制刷新。
如果你连短期都不能容忍,可以配置nginx:
bashlocation / { add_header Cache-Control no-store; }
no-cache从字面意义上很容易误解为不缓存,但是no-cache代表不缓存过期的资源,缓存会向服务器进行有效处理确认之后处理资源,使用no-cache的目的就是为了防止从缓存中获取过期的资源。
no-store才是真正的不进行缓存。
disk cache 也叫 HTTP cache,顾名思义是存储在硬盘上的缓存,因此它是持久存储的,是实际存在于文件系统中的。而且它允许相同的资源在跨会话,甚至跨站点的情况下使用,例如两个站点都使用了同一张图片。
disk cache 会严格根据 HTTP 头信息中的各类字段来判定哪些资源可以缓存,哪些资源不可以缓存;哪些资源是仍然可用的,哪些资源是过时需要重新请求的。当命中缓存之后,浏览器会从硬盘中读取资源,虽然比起从内存中读取慢了一些,但比起网络请求还是快了不少的。绝大部分的缓存都来自 disk cache。
关于 HTTP 的协议头中的缓存字段,我们会在稍后进行详细讨论。
优点:
缺点:
如何触发:
Service Worker 能够操作的缓存是有别于浏览器内部的 memory cache 或者 disk cache 的。我们可以从 Chrome 的 F12 中,Application -> Cache Storage 找到这个单独的“小金库”。除了位置不同之外,这个缓存是永久性的,即关闭 TAB 或者浏览器,下次打开依然还在(而 memory cache 不是)。有两种情况会导致这个缓存中的资源被清除:手动调用 API cache.delete(resource) 或者容量超过限制,被浏览器全部清空。
Service Worker 是运行在浏览器背后的独立线程,一般可以用来实现缓存功能。
Push Cache(推送缓存)是 HTTP/2 中的内容,当以上三种缓存都没有命中时,它才会被使用。
Service Worker ➔ Memory Cache ➔ Disk Cache ➔ 向服务器请求
访问图片为例: 访问 -> 200 -> 退出浏览器 -> 再进来 -> 200(from disk cache) -> 刷新 -> 200(from memory cache)
刚刚我们通过位置了解了几个类型,下面再通过失效策略分类了解缓存机制。 在失效策略分类中,又分为:强制缓存(强缓存) 和 协商缓存(对比缓存)。
不会向服务器发送请求,直接从缓存中读取资源。
在chrome控制台的Network选项中可以看到该请求返回200的状态码,并且Size显示from disk cache或from memory cache。
强缓存可以通过设置两种 HTTP Header 实现:Expires 和 Cache-Control。
这是 HTTP 1.0 的字段,表示缓存到期时间,是一个绝对时间 (当前时间+缓存时间),示例:
Expires: Thu, 10 Nov 2017 08:45:11 GMT
在响应消息头中,设置这个字段之后,就可以告诉浏览器,在未过期之前不需要再次请求。
但是,这个字段设置时有两个缺点:
max-age和Expires同时存在,则被Cache-Control的max-age覆盖。已知Expires的缺点之后,在HTTP/1.1中,增加了一个字段Cache-control,该字段表示资源缓存的最大有效时间,在该时间内,客户端不需要向服务器发送请求。
配置示例:
Cache-control: max-age=2592000
Cache-control 常用值说明:
max-age:最大有效缓存时间must-revalidate:如果超过了 max-age 的时间,浏览器必须向服务器发送请求,验证资源是否还有效。no-cache:缓存内容,但必须向服务端验证有效性后再使用no-store: 真正意义上的禁止所有缓存,不走强制缓存、协商缓存public:所有的内容都可以被缓存 (包括客户端和代理服务器, 如 CDN)private:所有的内容只有客户端才可以缓存,代理服务器不能缓存,默认值。max-age是HTTP/1.1中,指web文件被用户访问(请求)后的存活时间,是个相对值,相对请求时间(Request_time)。 例:18:00发布,Expires和max-age都设置10分钟过期; 18:05再次请求页面,18:11刷新时,只有Expires缓存过期更新,max-age需要18:15之后才会更新。
max-age=0 和 no-cache 等价吗?
从规范字面意思:max-age到期需要重新验证,no-cache是必须重新验证;
实际浏览器表现中,二者行为基本一致;
max-age=0, must-revalidate 则和 no-cache 完全等价。
总结: 自从 HTTP/1.1 开始,Expires 逐渐被 Cache-control 取代。
当强制缓存失效时,启用协商缓存,由服务器最终判定缓存是否失效。
核心流程: 浏览器携带缓存标识请求服务端 ➔ 服务端对比标识:
304 状态码,客户端继续使用本地缓存200 状态码 + 最新资源 + 全新缓存规则对比缓存请求次数和无缓存一致,但304响应无响应体,极大减少传输体积,节省带宽与响应时间;
协商缓存是强缓存的兜底方案,项目中通常搭配使用。
协商缓存分为两组实现方案:
Last-Modified & If-Modified-SinceEtag & If-None-MatchLast-Modified 响应头,返回资源最后修改时间
Last-Modified: Mon, 10 Nov 2018 09:10:11 GMT
If-Modified-Since 带上上次的修改时间304200 + 新资源存在缺陷:
为解决 Last-Modified 缺陷,HTTP/1.1 新增 Etag 机制:
If-None-Match 携带旧Etag优先级:Etag 高于 Last-Modified
ETag 核心优势:
html<!-- meta 禁用缓存示例 --> <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" /> <meta http-equiv="Pragma" content="no-cache" /> <meta http-equiv="Expires" content="0" />
更多文章参考:
https://zhuanlan.zhihu.com/p/44789005 https://blog.csdn.net/weixin_43972437/article/details/105513486