一、web缓存(前端缓存)
相同的数据背重复请求不止一次,多于请求会浪(为啥需要缓存)
(1)定义
web缓存就是存在于客户端与服务器之间的一个副本、当你第一个发出请求后,缓存根据请求保存输出内容的副本。
(2)分类
前端缓存
主要是分为HTTP缓存
和浏览器缓存
。其中HTTP缓存是在HTTP请求传输时用到的缓存,主要在服务器代码上设置;而浏览器缓存则主要由前端开发在前端js上进行设置。
(2.1)浏览器缓存
浏览器缓存则主要由前端开发在前端js上进行设置。
浏览器缓存:比如:localStorage,sessionStorage,cookie等等。这些功能主要用于缓存一些必要的数据,比如用户信息。比如需要携带到后端的参数。亦或者是一些列表数据等等。
不过这里需要注意。像localStorage,sessionStorage这种用户缓存数据的功能,他只能保存5M左右的数据,多了不行。cookie则更少,大概只能有4kb的数据。
(2.2)http缓存
HTTP缓存是在HTTP请求传输时用到的缓存,主要在服务器代码上设置。
http缓存是web缓存的核心,是最难懂的部分,也是最重要的部分。
(2.3)CDN缓存
(2.4)代理服务器缓存
(3)缓存的优缺点
服务器需要处理http的请求,并且http去传输数据,需要带宽。而我们缓存,就是为了让服务器不去处理这个请求,客户端也可以拿到数据。
注意,我们的缓存主要是针对html,css,img等静态资源,常规情况下,我们不会去缓存一些动态资源,因为缓存动态资源的话,数据的实时性就不会不太好,所以我们一般都只会去缓存一些不太容易被改变的静态资源。
缓存的好处(解决的问题):
- 省钱:减少不必要的http的请求,节约宽带
- 减载:减少服务器负载,避免服务器过载的情况出现
- 加速:降低网络延迟,更快的加载页面(直接读取浏览器的数据)
缺点:
- 占内存(有些缓存会被存到内存中)
二、http缓存
(1)缓存原理
缓存的原理是在首次请求后保存一份请求资源的响应副本,当用户再次发起相同请求后,如果判断缓存命中则拦截请求,将之前存储的响应副本返回给用户,从而避免了重新向服务器发起资源请求。
(2)http缓存分类与流程
HTTP缓存应该算是前端开发中最常接触的缓存之一,它又可以细分为强制缓存和协商缓存,二者最大的区别在于判断缓存命中时,浏览器是否需要向服务器端进行询问以协商缓存的相关信息,进而判断是否需要就响应内容进行重新请求,下面让我们来看看HTTP缓存的具体机制及缓存的决策策略。
http缓存流程图:
- 当浏览器发起一个资源请求时,浏览器会先判断本地是否有缓存记录,如果没有会向浏览器请求新的资源,并记录服务器返回的last-modified。
- 如果有缓存记录,先判断强缓存是否存在(cache-control优先于expires,后面会说),如果强缓存的时间没有过期则返回本地缓存资源(状态码为200)
- 如果强缓存失效了,客户端会发起请求进行协商缓存策略,首先服务器判断Etag标识符,如果客户端传来标识符和当前服务器上的标识符是一致的,则返回状态码 304 not modified(不会返回资源内容)
- 如果没有Etag字段,服务器会对比客户端传过来的if-modified-match,如果这两个值是一致的,此时响应头不会带有last-modified字段(因为资源没有变化,last-modified的值也不会有变化)。客户端304状态码之后读取本地缓存。如果last-modified。
- 如果Etag和服务器端上的不一致,重新获取新的资源,并进行协商缓存返回数据。
(3)缓存决策及注意事项
注意事项:
假设在不考虑客户端缓存容量与服务器算力的理想情况下,我们当然希望客户端浏览器上的缓存触发率尽可能高,留存时间尽可能长,同时还要Etag实现当资源更新时进行高效的重新验证。但实际情况往往是容量与算力都有限,因此就需要制定合适的缓存策略,来利用有限的资源达到最优的性能效果,明确能力的边界,力求在边界内做到最好。
在面对一个具体的缓存需求时,我们可以参照如下的缓存决策树来逐步确定对一个资源具体的缓存策略。
-
是否使用缓存
-
否:no-store
-
是:
-
是否进行协商缓存
-
是:no-cache
-
否
-
是否会被代理服务器缓存
-
是:public(
-
否:private
-
配置强制缓存过期时间
- 配置协商缓存的Etag或last-modified。
-
-
-
-
-
-
no-store:禁止使用缓存
no-cache:协商缓存
public:响应资源既可以被浏览器缓存,又可以被代理服务器缓存
private:限制了响应资源只能被浏览器缓存
public与private互斥,不设置默认是private
Etag:比较文件指纹(由文件内容计算出的唯一哈希值)
last-modified:比较修改资源文件的时间戳
Etag并不是last-modified的完全替代方案,而是补充方案,具体用哪一个,取决于业务场景。
缓存位置
从缓存位置上来说分为四种,并且各自有优先级,当依次查找缓存且都没有命中的时候,才会去请求网络。
-
Service Worker
- 行在浏览器背后的独立线程,一般可以用来实现缓存功能
- 因为 Service Worker 中涉及到请求拦截,所以必须使用 HTTPS 协议来保障安全
- Service Worker 的缓存与浏览器其他内建的缓存机制不同,它可以让我们自由控制缓存哪些文件、如何匹配缓存、如何读取缓存,并且缓存是持续性的。
-
Memory Cache
- 内存中的缓存,主要包含的是当前中页面中已经抓取到的资源,例如页面上已经下载的样式、脚本、图片等。
- 读取内存中的数据肯定比磁盘快,内存缓存虽然读取高效,可是缓存持续性很短,会随着进程的释放而释放。
- 一旦我们关闭 Tab 页面,内存中的缓存也就被释放了
- 访问过页面以后,再次刷新页面,可以发现很多数据都来自于内存缓存
-
Disk Cache
- Disk Cache 也就是存储在硬盘中的缓存,读取速度慢点
- Disk Cache 比Memory Cache胜在容量和存储时效性上。
- 在所有浏览器缓存中,Disk Cache 覆盖面基本是最大的
- 它会根据 HTTP Herder 中的字段判断哪些资源需要缓存,哪些资源可以不请求直接使用,哪些资源已经过期需要重新请求。
-
Push Cache
- Push Cache是推送缓存,是 HTTP/2 中的内容
- 只在会话(Session)中存在,一旦会话结束就被释放,并且缓存时间也很短暂
- 在Chrome浏览器中只有5分钟左右,同时它也并非严格执行HTTP头中的缓存指令。
如果以上四种缓存都没有命中的话,那么只能发起请求来获取资源了。
那么为了性能上的考虑,大部分的接口都应该选择好缓存策略,通常浏览器缓存策略分为两种:强缓存和协商缓存,并且缓存策略都是通过设置 HTTP Header 来实现的。
(4)http缓存-强缓存及协商缓存
- 当浏览器需要向服务器发送一个请求拿资源的时候,会先看本地有没有这个资源缓存
- 如果有,就需要看cache-contrl,expire来看资源有没有过期,如果没有过期,直接用本地不用发送了,这个就是强缓存
- 如果发现过期了,就需要把last-modified,etag字段发给服务器,服务器通过这二个字段决定本地资源到底能不能继续使用,如果能,就返回304,如果不能,就返回一个新的资源,就是200,这就是协商缓存
- 强缓存本质看资源有没有过期 协商缓存本质是看过期的资源能不能继续使用
下面这文字是为了理解,不用记
- 先看保质期 如果没有过 绝对可以吃 一个资源如果没有过最后期限 肯定不用再重新发请求 强缓存(expire 2023-1-1过期,Cache-Control 有效期1年 以资源拿回来的时间开始) Cache-Control优先级更高
- 如果过期 不一定,可以吃一小口确定味没有变 吃 一个资源过期了,需要和服务器商量一下 确定到底需要重新拿资源 协商缓存(Last-Modified etag只有文件内容没有变,etag就是唯一)etag优先级更高
四、面试常见问题
1、强缓存涉及到哪些请求头?
涉及到expires和cache-control两个字段,expires是HTTP1.0协议中的,cache-control是HTTP/1.1协议的。
2、为什么现在不用expries用cache control?
答:因为基于expires的强制缓存对本地时间戳过于依赖,如果客户端本地的时间与服务器端的时间不同步,那么对缓存过期的判断可能就会出错。cache-control通过maxage=xxx秒的形式来控制响应资源的有效期,如此可以避免服务器端和客户端时间戳不同步的问题。
3、强缓存public private no-store no-catch区别?(Cache-Control有哪些属性?分别表示什么意思?)
- public:表示响应资源既可以被客户端缓存也可以被代理服务器缓存。
- private:表示响应资源只能被浏览器缓存,如果没有显式指定则默认是private
- no-store:表示禁止使用任何缓存,每次请求都需要服务器给与全新的响应。
- no-cache:表示使用协商缓存。每次请求不再去判断强制缓存是否过期,而是直接向服务器发送请求来验证缓存的有效性。
- max-age:表示服务器端告知客户端浏览器响应资源的过期时长。
- s-maxage:表示缓存在代理服务器中的过期时长,且仅当设置了public属性值时才是有效的。
4、协商缓存的校验是在客户端还是服务器端?协商缓存怎么验证是否命中?
答:协商缓存的校验是服务器端。
命中协商缓存:服务器端会对比文件最后的修改时间和客户端请求携带的时间是否一致,一致则判断命中缓存。
协商缓存存在两种形式:
- 一种是基于
last-modified
,客户端第一次请求目标资源的时候,服务器返回的响应标头中包含last-modified
和该资源的最后一次修改的时间戳,以及cache-control:no-cache
,当客户端再次请求该资源的时候,会携带一个if-modified-since
字段,如果这个字段对应的时间和目标资源的时间戳进行对比,没有变化则返回304
状态码。 - 另一种是基于
Etag
的协商缓存,手下服务端将要返回给客户端的数据通过etag
模块进行哈希计算
生成一个字符串,这个字符串类似于文件指纹
,检测客户端的请求标头中的if-None-Match
字段的值和第一步计算的值是否一致,一致则返回304
,不一致则返回最新的数据以及etag
标头和Cache-Control:no-cache
。
5、协商缓存出于什么原因有Last-Modified,Etag?
答:之所以有last-modified还有etag,是因为这二者均有自己的不足,last-modified是根据请求资源的最后修改时间戳来进行判断的,有可能只是对文件名进行了编辑,但是文件内容并未修改,这样时间戳也会更新,从而导致协商缓存判断失效,请求了已经存在的完整资源,这对网络带宽是一种浪费,也有可能是文件修改的速度是毫秒级别的,但是last-modified的单位是秒,可能无法识别出资源的修改。etag并非last-modified的完全替代方案,只能是一种补充方案,etag存在的问题是,服务器需要对文件资源进行etag计算,需要付出额外的计算开销,如果资源的尺寸比较大,生成Etag的过程可能会影响服务器的性能,所以这也就是为什么协商缓存既有last-modified又有etag的原因了。
6、协商缓存和强缓存的区别?
相同点:都是从客户端缓存中读取资源。
不同点:
- 如果浏览器命中的是强缓存,则不需要给服务器发请求,而协商缓存最终由服务器来决定是否使用缓存,即客户端与服务器之间存在一次通信。
- 在chrome中命中缓存,返回的状态码是200,而如果是协商缓存,返回的是状态码304。
7、expires 和 cache-control 哪个优先级高? 不缓存怎么设置?
- expires是HTTP/1.0的产物
- Cache-Control则是HTTP/1.1的产物
- 二者如果同时存在的话,Cache-Control优先级比Expires高。
- 不缓存则是通过Cache-Control:no-store设置。
8、LastModified 对应有个请求头是什么?
last-modified-since
9、缓存的优先级顺序?
Cache-Control > Expires > Etag > Last-Modified
10、缓存更新怎么办?
现在突然来一个需求,需要将h1的字体颜色修改为红色。我将index.css修改了,但是在访问网站时候并没有看到想看的效果,字体还是蓝色,因为除了html文件,其余资源使用的是强缓存,所以每次访问都是访问强缓存里面的资源(强缓存没过期期间),得强制刷新才能跳过缓存重新请求拿到最新的文件。 这样显然是不合理,那么该如何做呢? 之前html不是设置了协商缓存嘛,所以每次访问都会询问html文件是否是最新的,所以只要修改html文件里面的css引入地址就行了 比如:在后面加个版本号,因为每次html都会发送请求询问是否过期,所以就可以达到效果了。 这样确实可行,但是开发不可能就引用一个css文件,往往是多个的,我每次修改一个css文件,其余的版本也要跟着升级但其余的css根本没有改动啊,所以这种方式就不大行,只适用于这种简单的网站应用。
具体可参考:前端静态资源缓存与更新
11、后端怎么设置缓存?
如果需要浏览器强缓存,我们可以这样设置:
res.setHeader('Cache-Control', 'public, max-age=xxx');
复制代码
如果需要协商缓存,则可以这样设置:
res.setHeader('Cache-Control', 'public, max-age=0');
res.setHeader('Last-Modified', xxx);
res.setHeader('ETag', xxx);
复制代码
12、怎么让浏览器不缓存静态资源
浏览器禁用缓存(打开控制台,勾选no-cache),用浏览器隐私模式打开页面
- 设置请求头: Cache-Control: no-cache, no-store, must-revalidate
- 给请求的资源带上版本号
<link rel="stylesheet" type="text/css" href="../css/style.css?version=1.8.9"/>
复制代码
- 部分浏览器支持在HTML中禁用缓存
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate"/>
复制代码