浏览器缓存
前言
浏览器缓存的问题是面试中关于浏览器知识的重要组成部分,也是性能优化题目的一部分,但是不要被吓到,我话放到这里,就那么点东西,我这一篇文章基本上就涵盖了所有相关的知识点,认真看一遍,所有的问题都是纸老虎。
一、准备工作
1.1 拉取仓库
本篇文章因为涉及到了在服务端设置缓存的内容,所以需要一个服务端的项目,可以跟着我的这篇文章搭建自己的服务端项目,或者直接克隆我的仓库代码。直接拉取最新的 master 分支的代码即可。
1.2 搭建服务器
1.2.1 新建 cache 文件夹
1.2.2 index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
cache
</body>
</html>
1.2.3 index.js
const express = require('express')
const path = require('path')
const app = express()
app.get('/', (req, res) => {
res.sendFile(path.resolve(__dirname, 'index.html') )
})
app.listen(3000)
1.2.4 运行
npm run dev cache
为什么运行参数要加一个 cache?
这是因为我的项目设置的,加一个参数,可以运行其文件夹下面的文件,具体请看这篇文章
1.2.5 提交代码
二、准备知识
我们前端说的缓存一般有两种类型,一个是浏览器相关的缓存,一个是 CDN 的缓存,CDN 的缓存一般我们控制不了,是服务商设置的【如阿里云】,我们最多可以使用服务商的管理平台开启、关闭或者清空缓存。此外后端也有很多缓存比如 redis 缓存,但是我们可以先不用考虑。
所以,我们前端面试题目中经常出现的缓存基本都和浏览器有关,浏览器的缓存又有很多种我们来逐一击破。
2.1 前端缓存方式
前端缓存有一下几种方式
Service Workers 【可以缓存资源的请求,参考】
web Storage【LocalStorage/SessionStorage】
IndexedDB【这个相当于浏览器中的数据库,请看官网】
Cache API【这个我没用过,可以看官网,是实验性技术】
Http 缓存头【这个是本篇文章的重点】
本篇文章主要介绍使用 http 缓存头完成的缓存逻辑。
2.2 浏览器的多进程模型
在学习浏览器的缓存之前,我们需要先来学习一下浏览器的相关内容,内容不多,记住浏览器的主要的几个进程就可以。
浏览器主进程
网络进程
渲染进程
插件进程
GPU 进程
具体的内容可以看一下我的这篇文章。
一般来说和 http 缓存头相关的缓存内容是浏览器的【网络进程】控制的
2.3 缓存的目的
如果网络进程在发送请求之前,发现本地有有效的缓存的文件和要请求的 URL 地址所匹配,那么就不需要发起后续的请求了。所以缓存的目的是减少网络请求,减少服务器的压力,提高页面加载的速度,提高网络的性能。特别是对于一些静态资源,不经常改动,那就一次请求,然后缓存下来,直到这个资源有改动,我们再去服务端拉去新的资源。
三、http 请求头缓存
下面所说的所有关于缓存的内容都是针对前端 http 请求头缓存的。
3.1 缓存位置
常见的缓存位置有两个分别是 disk cache 和 memory cache
memory cache 存在内存中,关闭浏览器标签页就释放,容量小,读取速度快
disk cache 存在磁盘中,容量大,读取速度慢
什么资源存储在 disk 中,什么资源存储在memory 中是浏览器的规则 ,一般来说肯定是大的资源比如字体啥的存在disk中,小的资源存取比较频繁的放在 memory 中,貌似没必要知道具体的详细的规则。
具体的缓存存在什么地方,我们在开发者工具中可以看到
3.2 刷新缓存
刷新页面的时候,不通的方式对缓存的处理对比
地址栏回车/直接访问 URL 保留强缓存,保留协商缓存,走正常请求流程
点击浏览器刷新按钮 忽略强缓存,保留协商缓存
按f5【command + r】 忽略强缓存,保留协商缓存
ctrl + f5 【command + shift + r 】 忽略强缓存,忽略协商缓存,从服务器端请求最新资源【强制刷新】
如果你改了页面提测了,测试同学说,不对啊,页面没有更新!
这个时候,那么你就应该考虑是不是有缓存,一般你说 【强制刷新】试下呢?基本就好了
这个里面还有个问题,就是可以使用无痕模式/ 隐身模式,无痕模式在关闭浏览器窗口后会删除浏览会话的所有信息。
不存储历史记录
不存储表单数据
不存储 cookie
不存储缓存
注意!他这里的不存储是指在关闭无痕窗口之后不存储,不是你在无痕窗口打开的时候没有,比如说缓存吧,你在无痕窗口刷新页面该有缓存,还是会有缓存。
3.3 缓存类型
http 请求头的缓存就分为两种类型【强缓存】和【协商缓存】。先记住一点,强缓存是用浏览器本地的缓存,不需要服务器参与;协商缓存是需要和服务器协商的;但是本质两种缓存用的缓存文件还都是存在浏览器本地的,和服务器协商的目的是为了验证本地缓存是否有效。
而且这两种缓存的共同点是,都是用 http 请求头部控制,接下来我们分别详细介绍强缓存和协商缓存。
3.4 请求头部
缓存涉及的无论是响应头、请求头有且仅有下面几个
expires 【响应头】
cache-control【请求头】【响应头】
Etag【响应头】 / if-none-match【请求头】
last-modified【响应头】 / if-modified-since【请求头】
至于每个头部的取值和赋值的规则,下面的章节会有详细的介绍。
注意,http 头部是不区分大小写的。
四、强缓存
强缓存的涉及的请求头只有两个分别是 expires 和 cache-control,注意强缓存的状态码是200.
4.1 expires 响应头
expires 只作为响应头出现在 http 请求的头部,expires 响应头设置一个服务器的绝对过期时间【必须是GMT时间,且需要考虑时差】,在这个时间之前资源都不用重新请求。
这是一个服务器的绝对时间,所以一定是由设置在响应头上的。
4.1.1 代码模拟
(1)修改 index.html
(2) 修改 index.js
注意这里面有一个坑,就是 express 服务回自动带上 ETag 但是这个是属于协商缓存的内容,我们暂时还没有用到,所以需要先禁用!
const express = require('express');
const path = require('path');
const app = express();
// 先禁用协商缓存的头部
app.disable('etag')
app.get('/', (req, res) => {
res.sendFile(path.resolve(__dirname, 'index.html'));
});
app.get('/info', (req, res) => {
try {
// 设置一分钟后过期
const now = new Date();
// 计算时差
const now2 = now.setTime(
now.getTime() - now.getTimezoneOffset() * 60 + 1 * 60
);
// 转换成 GMT 时间
const expiresDate = new Date(now2).toUTCString()
// 设置响应头
res.setHeader('expires', expiresDate);
res.send('ok');
} catch (err) {
console.log(err);
}
});
app.listen(3000);
(3)运行
npm run dev cache
我们的服务设置的 info 请求一分钟后过期
我们来看一下使用缓存的第二个请求的请求头是啥样的,注意这个时候返回的状态码依旧是 200,代表请求成功。
至此关于强缓存的第一个 http 头部,响应头 expries 就介绍完了。
(4)提交代码
4.2 cache-control
和 expires 不同的是,cache-control 不仅可以服务端设置作为响应头,还可以客户端设置作为请求头,至于到底是客户端端设置,还是服务端设置,结论在这篇文章。
我们可以看 mdn 官方文档 cache-control 有很多取值,请求头和响应头还不一样;下表中标红的是常见的,在这篇文章中我们也只研究标红的请求/响应头。
请求头 响应头
1 no-cache no-cache 使用协商缓存
2 no-store no-store 不缓存
3 max-age max-age 强缓存失效后,使用协商缓存
4 no-transform must-revalidate
5 min-fresh no-transform
6 only-if-cached public
7 private
8 proxy-revalidate
9 s-maxage
常用的头部其实只有3个,分别是 no-cache、no-store、max-age
cache-control取值 说明
no-cache 在发布缓存副本之前,强制要求缓存把请求提交给原始服务器进行验证 (协商缓存验证)。
no-store 缓存不应存储有关客户端请求或服务器响应的任何内容,即不使用任何缓存。
max-age=<seconds> 设置缓存存储的最大周期,超过这个时间缓存被认为过期 (单位秒)。与Expires相反,时间是相对于请求的时间。max-age =0 实际上和 no-cache 的行为一致
这个怎么记呢,尤其是 no-cache 和 no-store 总是容易弄混?
记住这个单词 store 有存储的意思,no-store,那就是不存储,无论如何都不存储,所以no-store 更狠一些,不存储任何东西,不使用任何缓存。
看看官网怎么说
The "no-store" request directive indicates that a cache MUST NOT store any part of either this request or any response to it. This directive applies to both private and shared caches. "MUST NOT store" in this context means that the cache MUST NOT intentionally store the information in non-volatile storage, and MUST make a best-effort attempt to remove the information from volatile storage as promptly as possible after forwarding it. “no-store”请求指令表明缓存不能存储此请求的任何部分或对其的任何响应。 这指令适用于私 有和共享缓存。
“一定不在此上下文中“存储”意味着缓存不得故意将信息存储在非易失性存储器中,并且必须制作 ,一个尽最大努力尝试从易失性存储中删除信息 转发后尽快。
对比之下 no-cache 就温和一点了,不使用缓存,但是还有协商缓存这一个口子
4.3 cache-control 响应头
我们先来看响应头设置 cache-control 的情况,对于强缓存的响应头其实只有一个 max-age
且max-age的值不能为0,max-age=0 等价于 no-cache
4.3.1 修改 index.html
4.3.2 no-store 响应头
no-store 响应头不做任何缓存,无论发送多少请求都是重新请求
4.3.3 no-cache 响应头
4.3.4 max-age
max-age 的单位是秒
(1)max-age=0
(2)max-age 不等于0
这个才是真正的强缓存,和之前说的 expires 头部一样,真正的强缓存。
设置 max-age=10 10秒之后过期。
4.4 cache-control 请求头
4.4.1 no-store
我们在之前的代码基础上,不修改 index.js 保留响应头 max-age=10 的设置
我们发现了,即便是请求头设置了不使用缓存,浏览器依旧使用了响应头的强缓存,这个就很神奇了。
4.4.2 no-cache
把请求头设置为 no-cache
4.4.3 max-age=0
4.4.4 max-age 不等于0
客户端和服务端同时设置了max-age,这里面以服务端设置的响应头为准了。
我们接下来吧响应头的 max-age去掉,仅保留请求头的,发现强缓存并未生效。
所以我得出了一个结论,就是在客户端设置 max-age 请求头值为非 0 的强况下,强缓存不会生效。
至少我测试的 express + chrome 浏览器是这样的,不通的浏览器可能对于缓存有不同的规则。
4.5 请求头 vs 响应头
通过上面的例子,我么你注意到 cache-control 的同一个值可以即由客户端设置在的请求头,又可以由服务度设置在响应头。但是你要问了,到底设置在哪边呢?
我的建议是不要纠结,同时设置到底以那个为准了,不通浏览器貌似有不同的处理。
对于客户端和服务端同时设置 cache-control 不通的值的时候还有很多组合,但是感觉没有逐个测试的意义,我们只要只要每个值的含义就行了,在实际应用过程中,如果客户端设置的不满足需求,我们就使用服务端设置就行了。
如果你还有其他的见解,也可以在评论区发表观点,我们一起讨论下。
4.6 expires 和 cache-control
强缓存涉及到两个 http 头部,分别是 【expires】和【cache-control】其中【cache-control】的优先级高于【expires】,这是因为 【expires】是在http1.0 提出来的,【cache-control】是在http1.1 提出的。二者同时存在的时候以【cache-control】为准。
4.6.1 修改 index.html
4.6.2 修改 index.js
4.7 刷新强缓存
(1)使用浏览器的强制刷新功能就可以强制刷新缓存,比如 按下ctrl + f5【mac 电脑是 command+shift +R】,浏览器会忽略所有缓存,也不执行协商缓存。
(2)使用开发者工具禁用缓存,注意这个只在开发者工具打开的时候有效!关掉开发者工具,刷新缓存就只能用强制刷新。开发者工具的这个禁用缓存浏览器会忽略所有缓存,也不执行协商缓存。
小结
至此,强缓存相关的知识已经总结完毕,结论是设置响应头为 max-age 且值不为0 或者expires 时会使用强缓存。
五、协商缓存
如果命中强缓存,那就直接使用缓存内容即可,不需要发起后续请求,但是上面我们提到了,如果cache-control 设置了 no-cache / 或者 max-age=0 / 或者 max-age =xxx 过期了,就需要进行协商缓存。
5.1 协商缓存条件
协商缓存的条件有
cache-control: no-cache 或者
cache-control: max-age=xxx (单位是秒) 过期了/或者是 =0
协商缓存涉及到的请求头有 2 种方式,这两组一般是选其中一种,但是也可以同时设置,这两组请求头的第一个字段是需要后端在服务器代码中加上的(last-modified或者etag);第二个字段是浏览器根据上一个请求的响应头的 last-modified 或者 etag,自动设置的,不需要人工参与。
我们前端只需要在请求头上设置 cache-control 的值为 no-cache,或者设置 max-age=0,也要服务端配合设置响应头,具体的规则参考这篇文章。
注意 no-cache 和 max-age 是两个c ache-control 字段的取值,他俩可以同时使用(使用逗号分隔),也可以只使用其中的一个。
(1)仅设置了 no-cache 那么每次使用缓存之前,都需要去服务器确认资源;
(2)仅设置了 max-age 在未过期之前不需要向服务器确认资源的有效性;
(3)no-cache 和 max-age 同时设置的时候 no-cache 生效,每次都需要去服务器进行资源验证
如果你是服务端开发者,你就需要从下面 2 对协商缓存头中选一对。但是一般如果是使用的服务端的框架,对于缓存框架有默认的内部的处理,不需要手动设置,比如 express 就会默认携带etag,详情请看4.1.1,我们手动禁用了express 中的Etag
5.2 协商缓存头部
5.2.1 Last-Modified / If-Modified-Since【优先级低】
当浏览器首次请求一个资源时,服务器会返回资源的最后修改时间(服务端在响应头设置 Last-Modified)。当浏览器再次请求该资源时,会在请求头中自动包含 If-Modified-Since 字段,该字段的值是上次服务器返回的最后修改时间。如果资源在这个时间之后没有发生变化,服务器会返回状态码 304(Not Modified),告诉浏览器可以使用本地缓存。
Last-Modifed 的精确度是秒,所以一秒内做出的修改会被判定文件未修改,不太精确,所以提出了 ETag。
这里面说的【服务器返回】,就不是浏览器的功能了,是后端开发人员手动写的,比如我们手动写的 express 服务 需要手动设置,last-modifed 的取值需要时 GMT 时间。
res.setHeader('Last-Modified', xxx)
然后还需要获取请求头部的 If-Modified-Since 并且和 服务端保留的上一个请求的 last-mofified 字段进行对比,如果没有修改返回 304。大概的代码如下,这个例子使用的是express
let lastModifed = ''
app.get('/auth', function (req, res, next) {
res.setHeader('cache-control', 'max-age=20')
// 获取浏览器自动 加上的请求头
const ifModifiySince = req.headers['if-modified-since']
// 判断内容是否被修改
// lastModifed 注意这个字段是上一个请求的
if (ifModifiySince === lastModifed) {
res.status(304) // 给浏览器返回304 状态码
res.end('end')
return
}
lastModifed = new Date().getTime()
// 服务端设置 Last-Modified,这是给下一个请求设置的
res.setHeader('Last-Modified', lastModifed)
res.end('ok');
});
如果是我们自己实现的服务端,那么就需要有这块的逻辑,但是如果用的后端的框架,一般框架都会带有这些逻辑,就不用自己写了。
5.1.2 ETag / If-None-Match 【优先级高】
类似于 Last-Modified,服务器在响应头返回资源的唯一标识符(这个标识符由服务端自己定义可,一般是基于资源内容的哈希值),称为 ETag。浏览器在后续请求中,会在请求头中自动包含 If-None-Match 字段,该字段的值是上次服务器返回的ETag。如果资源的ETag匹配,服务器同样可以返回状态码 304,告诉浏览器可以使用本地缓存。
如果 Last-Modified 和 ETag 同时存在,那么优先判断 ETag / if-none-match,因为ETag 是http1.1 提出来的。
ETag 优先的意思是,判断完是否有 ETag ,如果有就不判断 last-modified了,如果没有 Etag 再判断 last-modifed
5.3 代码模拟
首先我们把所有的强缓存的头部去掉,客户端和服务端都不设置任何强缓存头部。
5.3.1 Last-Modified / If-Modified-Since
Last-Modified 的值需要时 GMT 时间,是文件的最后修改时间,这一点和 expires 是一样的,但是其实我们可以设置任意值(不过不建议),因为对比 Last-Modified 和 If-Modified-Since 的逻辑是我们的服务端自己写的的。如果我们设置的值是非时间格式的,那么其实就和 ETag 一样了。
(1)修改 index.html
(2) 修改 index.js
(3)运行结果
字段给带上了,但是还没有使用缓存,因为 304 逻辑需要服务端自己实现。
(3)代码实现使用缓存
304 这个状态码记住,就是未修改的意思。
5.3.2 ETag / if-none-match
如果上面的 last-modified 不设置为时间,那么就是和 ETag 的逻辑一致。
(1)修改代码
(2)运行结果
5.4 小结
注意,我们整个协商缓存的代码都没有设置 cache-control, 也就是说可以理解为协商缓存的头部不是依赖于 cache-control 的,但是cache-control 如果设置了 no-cache/max-age=0/max-age 过期,接下来就会判断是否有协商缓存的头部。
5.4.1 强缓存+协商缓存
我们以 max-age 为例试一下。
六、面试题
这些面试题目有很多重复的内容,同一个知识点重复学习即便肯定会加深印象的。
6.1 浏览器有哪几种缓存,各种缓存的优先级是什么样的?
浏览器的缓存主要分为四种分别是
强缓存:通过 http 响应头 cache-control: max-age 和expires 字段强制使用本地资源
协商缓存:在 max-age 过期或者 cache-control: no-cache 的时候,向服务发送资源验证配合请求头 Last-modified / if-modifed-since 和 ETag / if-none-match进行验证,其中 ETag 的优先级高于 Last-modified,匹配到协商缓存资源之后一般返回304(not modifed)
service worker:是独立于 js 主线程运行的js脚本,可以拦截网络请求,缓存响应,以实现离线访问和更快的加载速度,可以离线使用
web storage:使用浏览器本地存储功能 localstorage / indexDB 实现数据的缓存,可以离线使用。
优先级 server worker > 强缓存 > 协商缓存 > web storage ,因为service worker 缓存可以完全控制网络请求,所以优先级最高,强缓存也会被覆盖。
6.2 从存储位置看,浏览器缓存分为那几种
从缓存位置看,浏览器缓存分为5种
service worker service worker 是浏览器运行的独立线程,会拦截所有的网络请求(包括图片、资源等请求都能拦截),优先级很高,可以实现缓存功能,还可以自定义响应文件 responseWith。
memory cache 浏览器内存中的缓存,关闭tab页,就释放,一般是小文件
disk cache 存储在硬盘中,速度慢,一般存放大文件
push cache 推送缓存是http/2中的内容,当没有上述3种缓存时,才会被使用,他只会在会话session种存在,一旦会话结束就被释放
web storage / indexedDB 开可以放 web storage 和 indexedDB 中
6.3 协商缓存中,有了 Last-Modified 为什么还会出现 ETag
因为 ETag 可以解决 Last-Modified 无法解决的问题
【精确度】ETag 的精确度更高。某些服务器不能精确得到文件的最后修改时间(或者,对于频繁修改的文件只能精确到秒), 这样就无法通过最后修改时间来判断文件是否更新了
【效率高】对于动态生成内容的服务器来说,ETag 可以更有效地表示资源的状态。
一些文件的最后修改时间改变了,但是内容并未改变。 我们不希望客户端认为这个文件修改了,所以可以使用 ETag
但是 Etag 在负载均衡的分布式系统中有问题,可能不同服务器 ETag 不同,但是最后修改时间都是从数据库中取出的,所以负载均衡系统中的 Last-Modified 是相同的。
ETag 的优先级高于 Last-Modified
6.4 什么是前端资源版本控制?你会如何实现它?
前端资源版本控制是一种管理和更新前端静态资源(css、js、图片等)的方法,以确保客户端能够获取最新版本的资源,同时有效的利用浏览器缓存,减少不必要的网络请求。主要目的是避免浏览器使用旧版本的缓存而导致页面展示不正确或性能下降的问题。
前端资源版本控制的方法通常包括
【文件名哈希】:在文件名种添加哈希值,根据文件内容的变化生成卫衣的文件名,例如,main.css 可以变成 main.abc123.css,这样,每次文件内容变化时,文件都会被改变,强制浏览器重新下载新版本的文件。在构建过程中,可以使用打包工具webpack和vite等来自动为文件添加哈希
【查询参数】:在资源引用的URL中添加查询参数,例如 main.css?v=abc123。查询参数的变化会使浏览器认为这是一个新的URL,从而重新加载资源。这种方式通常需要服务器配置,确保查询参数不影响资源的实际内容,而只是用于版本控制。
【服务端控制】:通过在服务端设置响应头cache-control、expires,强制浏览器重新获取资源。通过服务器端的脚本或配置,在每次发布新版本时修改资源引用的路径,强制客户端获取新版本的资源。
【CDN缓存刷新】:如果使用了内容分发网络(CDN),可以通过刷新 CDN 缓存来确保新版本的资源被传递给用户。这可以通过CDN提供商的控制台或API来完成。
6.5 强缓存和协商缓存分别是什么
可以使用 cache-control 或者 expires 控制浏览器的缓存,其中 expires 是http1.0种提出的,cache-control 在http1.1协议中提出,cache-control 的优先级高于 expires。
强缓存
(1)expires 请求头指定一个服务器端的绝对过期时间,如果在有效期内,就可以使用缓存资源,如果过期了就发起请求
(2)cache-control 设置值为max-age=xx(单位时秒)来实现强缓存,cache-control可以设置很多值,当设置max-age不为0 的时候,使用本地缓存,过期之后进行协商缓存的流程
协商缓存
当强缓存失效后,会进行协商缓存,当 cache-control 设置了 no-cache 或者 max-age=0 或者 max-age=xx过期之后就会进行协商缓存。当然协商缓存的前提是,服务端会在第一次请求之后在响应头加上协商缓存的字段:
服务端在响应头设置 last-modified,下一次请求客户端在请求头带上 if-modified-since,服务端接到请求后会判断资源是否被修改,如果没有修改返回 304 然后浏览器使用缓存,如果修改了,会继续请求最新的资源。
服务端在响应头设置 ETag,下一次请求客户端在请求头带上 if-none-match,服务端接到请求后会判断资源是否被修改,如果没有修改返回 304 然后浏览器使用缓存,如果修改了,会继续请求最新的资源。
ETag 的优先级高于 Last-Modified
6.6 解释一下常见的缓存控制头有哪些?
no-store 不使用任何缓存,不缓存任何资源
no-cache 不使用强缓存,使用协商缓存
max-age 使用强缓存,过期之后使用使用协商缓存
must-revalidate(响应头) 【重新验证】资源过期,在成功向原始服务器验证之前,缓存不能用该资源响应后续请求
public (响应头) 表明响应可以被任何对象缓存,即使是通常不可缓存的内容(例如:1.该响应没有max-age指令或Expires消息头;2. 该响应对应的请求方法是 POST 。)
指定 no-cache 或 max-age=0, must-revalidate 表示客户端可以缓存资源,每次使用缓存资源前都必须重新验证其有效性。这意味着每次都会发起 HTTP 请求,但当缓存内容仍有效时可以跳过 HTTP 响应体的下载。
6.7 Expires 头和 Cache-Control: max-age 头之间有什么区别?
Expires 是http1.0的功能,是一个服务端设置的绝对时间,在没有过期之前会使用本地缓存,但是有一个问题就是如果本地时间和服务器事件不统一缓存可能会失效。
cache-control是http1.1的功能,他的取值可以是max-age=秒,实现强缓存,在过期之前都会使用浏览器的缓存,如果过期了,就会进行协商缓存,max-age=0和设置no-cache是一样的会去服务端进行资源的验证,也就是协商缓存。
6.8 no-cache 和 no-store 的区别是什么?
no-cache 代表需要去服务器进行缓存资源有效性的检验,检验通过才使用缓存,验证不通过就继续发起请求获取最新的资源。
no-store 代表不使用任何缓存,既不用强缓存也不用协商缓存。
6.9 HTTP状态码中的 304 Not Modified 是什么意思?
304 代表未修改,是在协商缓存的过程中如果 last-modified === if-modified-since或者ETag===if-none-match 服务端一般会返回304状态,304请求没有相应题,代表请求的资源未被修改,可以使用缓存的资源,无需继续请求最新的资源。
6.10 Vary 头的作用是什么?
Vary HTTP 响应标头描述了除方法和 URL 之外影响响应内容的请求消息。大多数情况下,这用于在使用内容协商时创建缓存键。
Vary 头是HTTP响应头的一部分,它用于指定在使用缓存时是否考虑请求头中的某些字段。具体来说,Vary 头允许服务器通知缓存系统,哪些请求头字段应该被考虑在内以决定是否使用缓存中的响应。如果vary中的字段的值在不同的请求中有变化,那么缓存系统就会根据这些变化来决定是否使用缓存(也就是发生变化后不使用缓存)
作用:
缓存隔离: 通过 Vary 头,服务器可以指定某些请求头字段,使得缓存在使用缓存数据时考虑这些字段的值。这样可以实现对不同请求头条件下的缓存进行隔离,确保每个条件都有独立的缓存。
内容协商: 当同一URL对应的资源可以根据请求头的不同值提供不同的内容时,使用 Vary 头是很重要的。例如,根据请求头中的 Accept-Language 字段,服务器可能返回不同语言版本的内容。在这种情况下,服务器会在响应中包含 Vary: Accept-Language,以确保缓存系统根据不同的语言条件存储和检索缓存。
需要注意的是,使用 Vary 头时,要确保它覆盖了所有可能影响响应内容的请求头字段。如果漏掉了某个重要的字段,可能导致缓存不正确或者不生效。合理使用 Vary 头可以帮助开发者更好地管理缓存,提高系统的性能和可维护性。
6.11 怎样禁用浏览器缓存?
在开发者工具中禁用缓存(这个只在开发者工具打开的状态下有效)
使用cache-control: no-store 禁用所有缓存,no-cache、max-age=0、must-revalidate禁用强缓存
expires:0 禁用所有缓存
在请求的url上增加时间戳 url?time=xx
资源文件使用哈希文件名
强制刷新浏览器
6.12 如果要确保资源每次都是最新的,你会如何设置缓存策略?
expires:0
Pragma: no-cache 主要用于向旧版本的 HTTP/1.0 客户端发送缓存指令,但在一些情况下,也可以用来防止缓存。可以与 Cache-Control 一同使用。
Cache-Control: no-cache, no-store, must-revalidate, max-age=0,但是使用no-store其实是没有使用缓存
使用url加上时间戳参数请求
6.13 在浏览器中,用户清除缓存的行为会对网站有什么影响?
重新下载所有资源
增加服务器负载
延长页面加载时间
重新生成会话状态session
更新本地存储和indexedDB
重新触发首次访问行为
6.14 强缓存和协商缓存的 http 状态码分别是什么
强缓存命中的时候中状态码是200 ok,表示使用缓存的资源。
协商缓存在协商的过程中,服务端会返回 304 not modified 表示资源未被修改,如果资源修改了,返回新的资源,状态码就是200
6.15 如果不配置任何请求头,还会有缓存么
如果不配置 Cache-Control 和 Expires 等缓存控制头部,浏览器仍然可能会缓存响应内容,但具体的缓存策略将由浏览器自行决定,可能会因浏览器的不同而有所差异。
在这种情况下,浏览器可能会使用启发式算法来判断响应内容的缓存策略,通常会考虑以下因素:
资源的类型:对于静态资源(如图片、样式表、脚本等),浏览器可能会更倾向于缓存,因为这些资源相对稳定且不经常更改。
响应的状态码:一般情况下,2xx 状态码(如 200 OK)的响应会被浏览器缓存,而非 2xx 状态码(如 404 Not Found、500 Internal Server Error 等)的响应则不会被缓存。
响应的大小:较大的响应内容可能不太容易被浏览器缓存,因为它们可能会占用大量的存储空间。
用户的浏览器设置:浏览器可能会考虑用户的缓存设置(如隐私模式、清除缓存等),从而决定是否缓存响应内容。
总结
前端缓存,使用http请求头控制的缓存这块,如果用代码自己实现之后发现,就那么点东西。