浏览器缓存
2025-04-21 11:37:40 0 举报
AI智能生成
浏览器缓存不同视角的分类,最佳实践
作者其他创作
大纲/内容
概述
概述内容:浏览器的缓存机制也就是我们说的 HTTP 缓存机制,其机制是根据 HTTP 报文的缓存标识进行的
报文分类:在分析浏览器缓存机制之前,我们先使用图文简单介绍一下 HTTP 报文,HTTP 报文分为两种:
请求报文
响应报文
缓存过程分析(以第一次请求为例):
浏览器与服务器通信的方式为应答模式,即是:浏览器发起HTTP请求 – 服务器响应该请求。那么浏览器第一次向服务器发起该请求后拿到请求结果,会根据响应报文中HTTP头的缓存标识,决定是否缓存结果,是则将请求结果和缓存标识存入浏览器缓存中,简单的过程如图:
由图我们可以知道:
浏览器每次发起请求,都会先在浏览器缓存中查找该请求的结果以及缓存标识
浏览器每次拿到返回的请求结果都会将该结果和缓存标识存入浏览器缓存中
以上两点结论就是浏览器缓存机制的关键,他确保了每个请求的缓存存入与读取,只要我们再理解浏览器缓存的使用规则,那么所有的问题就迎刃而解了,本文也将围绕着这点进行详细分析。为了方便大家理解,这里我们根据是否需要向服务器重新发起HTTP请求将缓存过程分为两个部分,分别是强制缓存和协商缓存。
浏览器与服务器通信的方式为应答模式,即是:浏览器发起HTTP请求 – 服务器响应该请求。那么浏览器第一次向服务器发起该请求后拿到请求结果,会根据响应报文中HTTP头的缓存标识,决定是否缓存结果,是则将请求结果和缓存标识存入浏览器缓存中,简单的过程如图:
由图我们可以知道:
浏览器每次发起请求,都会先在浏览器缓存中查找该请求的结果以及缓存标识
浏览器每次拿到返回的请求结果都会将该结果和缓存标识存入浏览器缓存中
以上两点结论就是浏览器缓存机制的关键,他确保了每个请求的缓存存入与读取,只要我们再理解浏览器缓存的使用规则,那么所有的问题就迎刃而解了,本文也将围绕着这点进行详细分析。为了方便大家理解,这里我们根据是否需要向服务器重新发起HTTP请求将缓存过程分为两个部分,分别是强制缓存和协商缓存。
什么是缓存标识?
个人理解缓存标识就是缓存相关的头部字段,例如 cache-control、e-tag、expires 等
浏览器和浏览器缓存这部分的交互是自发的吗?
个人理解是的,这块由浏览器实现,对于前端开发者和用户来说都是没有感知的
强缓存
概念:强制缓存就是向浏览器缓存查找该请求结果,并根据该结果的缓存规则来决定是否使用该缓存结果的过程
说明:所谓的“强”与“弱”并不是字面意思,而是一个过程,大致区分标准是“是否需要向服务器发起 http 请求”,之所以是大致,是强缓存失效时也是需要向浏览器发起请求的,具体可以看分类
分类:强制缓存的情况主要有三种(暂不分析协商缓存过程)
不存在该缓存结果和缓存标识,强制缓存失效,则直接向服务器发起请求(跟第一次发起请求一致)
存在该缓存结果和缓存标识,但该结果已失效,强制缓存失效,则使用协商缓存(暂不分析)
存在该缓存结果和缓存标识,且该结果尚未失效,强制缓存生效,直接返回该结果
缓存规则
规则内容:当浏览器向服务器发起请求时,服务器会将缓存规则放入HTTP响应报文的HTTP头中和请求结果一起返回给浏览器,控制强制缓存的字段分别是Expires和Cache-Control,其中Cache-Control优先级比Expires高。
控制字段
Expires
概念:Expires是HTTP/1.0控制网页缓存的字段,其值为服务器返回该请求结果缓存的到期时间,即再次发起该请求时,如果客户端的时间小于Expires的值时,直接使用缓存结果。
说明:到了HTTP/1.1,Expire已经被Cache-Control替代,原因在于Expires控制缓存的原理是使用客户端的时间与服务端返回的时间做对比,那么如果客户端与服务端的时间因为某些原因(例如时区不同;客户端和服务端有一方的时间不准确)发生误差,那么强制缓存则会直接失效,这样的话强制缓存的存在则毫无意义
Cache-Control
概念:在HTTP/1.1中,Cache-Control是最重要的规则,主要用于控制网页缓存
取值
public:所有内容都将被缓存(客户端和代理服务器都可缓存)
private:所有内容只有客户端可以缓存,Cache-Control的默认取值
no-cache:客户端缓存内容,但是是否使用缓存则需要经过协商缓存来验证决定
no-store:所有内容都不会被缓存,即不使用强制缓存,也不使用协商缓存
兼容方案:作为忽略 no-store 的过时实现的解决方法,你可能会看到使用了诸如以下内容的 kitchen-sink 标头,参考右侧链接:
Cache-Control: no-store, no-cache, max-age=0, must-revalidate, proxy-revalidate
Cache-Control: no-store, no-cache, max-age=0, must-revalidate, proxy-revalidate
no-store 并不是退出缓存的最正确方法,不建议随意授予 no-store,因为你失去了 HTTP 和浏览器所拥有的许多优势,包括浏览器的后退/前进缓存。因此,要获得 Web 平台的全部功能集的优势,最好将 no-cache 与 private 结合使用。
常见的默认情况下始终传输最新版本资源的方法通常是 Cache-Control: no-cache ,参考右侧链接
常见的默认情况下始终传输最新版本资源的方法通常是 Cache-Control: no-cache ,参考右侧链接
max-age=xxx (xxx is numeric):缓存内容将在xxx秒后失效
计时说明:计时是以响应时间开始计算的,例如:
max-age=0 和 must-revalidate 的组合与 no-cache 具有相同的含义
其他取值:s-maxage、min-fresh 等,见
举例
由上面的例子我们可以知道:
HTTP响应报文中expires的时间值,是一个绝对值
HTTP响应报文中Cache-Control为max-age=600,是相对值
由于Cache-Control的优先级比expires,那么直接根据Cache-Control的值进行缓存,意思就是说在600秒内再次发起该请求,则会直接使用缓存结果,强制缓存生效。
注:在无法确定客户端的时间是否与服务端的时间同步的情况下,Cache-Control相比于expires是更好的选择,所以同时存在时,只有Cache-Control生效。
HTTP响应报文中expires的时间值,是一个绝对值
HTTP响应报文中Cache-Control为max-age=600,是相对值
由于Cache-Control的优先级比expires,那么直接根据Cache-Control的值进行缓存,意思就是说在600秒内再次发起该请求,则会直接使用缓存结果,强制缓存生效。
注:在无法确定客户端的时间是否与服务端的时间同步的情况下,Cache-Control相比于expires是更好的选择,所以同时存在时,只有Cache-Control生效。
缓存位置
说明:比较常见的是两种:memory cache(内存缓存) 和 disk cache(硬盘缓存)
分类
Memory Cache(内存缓存):Memory 为内存缓存,是浏览器最先尝试命中的缓存,也是响应最快的缓存。但是存活时间最短的,当进程结束后,tab 标签关闭后,缓存就不存在了。
Disk Cache(磁盘缓存):内存的优先性,导致大文件不能缓存到内存中,那么磁盘缓存则不同。虽然存储效率比内存缓存慢,但是存储容量和存储市场有优势。
Service Worker(离线缓存):Service Worker 是一种独立于主线程之外的 Javascript 线程。它脱离于浏览器窗体,因此无法直接访问 DOM。
Push Cache(推送缓存):它是最后一道缓存命中,属于 HTTP2 的内容.
优先级
Memory Cache(内存缓存)>Service Worker(离线缓存)>Disk Cache(磁盘缓存)>Push Cache(推送缓存)
协商缓存
概念:协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程
分类:分为两种情况
协商缓存生效,返回304
协商缓存失效,返回200和请求结果结果
缓存规则
规则内容:协商缓存的标识也是在响应报文的HTTP头中和请求结果一起返回给浏览器的,控制协商缓存的字段分别有:Last-Modified / If-Modified-Since和Etag / If-None-Match,其中Etag / If-None-Match的优先级比Last-Modified / If-Modified-Since高,同时存在则只有Etag / If-None-Match生效。
控制字段
Last-Modified / If-Modified-Since
Last-Modified
服务器响应请求时,返回该资源文件在服务器最后被修改的时间
If-Modified-Since
If-Modified-Since则是客户端再次发起该请求时,携带上次请求返回的Last-Modified值,通过此字段值告诉服务器该资源上次请求返回的最后被修改时间。服务器收到该请求,发现请求头含有If-Modified-Since字段,则会根据If-Modified-Since的字段值与该资源在服务器的最后被修改时间做对比,若服务器的资源最后被修改时间大于If-Modified-Since的字段值,则重新返回资源,状态码为200;否则则返回304,代表资源无更新,可继续使用缓存文件,如下图
Etag / If-None-Match
Etag
Etag是服务器响应请求时,返回当前资源文件的一个唯一标识(由服务器生成),如下
If-None-Match
f-None-Match是客户端再次发起该请求时,携带上次请求返回的唯一标识Etag值,通过此字段值告诉服务器该资源上次请求返回的唯一标识值。服务器收到该请求后,发现该请求头中含有If-None-Match,则会根据If-None-Match的字段值与该资源在服务器的Etag值做对比,一致则返回304,代表资源无更新,继续使用缓存文件;不一致则重新返回资源文件,状态码为200,如下
总结
内容:强制缓存优先于协商缓存进行,若强制缓存(Expires和Cache-Control)生效则直接使用缓存,若不生效则进行协商缓存(Last-Modified / If-Modified-Since和Etag / If-None-Match),协商缓存由服务器决定是否使用缓存,若协商缓存失效,那么代表该请求的缓存失效,重新获取请求结果,再存入浏览器缓存中;生效则返回304,继续使用缓存,见下方流程图
缓存流程图:
最佳实践
说明
因为浏览器判断缓存是否命中的一个关键是 url,url 资源包括了文件名,现代化的前端构建工具都会对静态资源的文件名带上 hash 值(例如对内容的 md5),一旦文件变更,hash 值就会改变,原有的文件缓存就一定不会被命中,这种方式使得我们可以放心的将除了 .html 以外的资源设置一个很长的过期时间。至于 index.html 之所以不能强缓存是因为前端的版本更新往往会更新此文件,新版本的此文件内引用的其他资源(例如 js css)也有着不同的 hash,只有保证这个“头文件”每次都是最新的,才能保证其依赖的其他资源是正确的版本
做法
.html 不做缓存,每次请求都获取最新版本,no-cache 虽然是每次都要去服务器协商的意思,但只要服务器每次响应 200 和新资源即可,至于为什么不用 no-store,详见 no-store 取值后面的说明
Cache-Control: no-cache
使用现代化构建工具构建的(这类工具对于静态资源都会进行修改文件名,加上一个内容的 hash)其他所有资源文件(包括 js、css和图片等),都做强缓存(一个月 2592000 打底,可以设置一年)
Cache-Control: max-age=2592000
相关链接:
mdn: http 缓存
mdn: cache-control
其他优秀网络链接
github
掘金
0 条评论
下一页