强制缓存,我们简称强缓存。
从强制缓存的角度触发,如果浏览器判断请求的目标资源有效命中强缓存,如果命中,则可以直接从内存中读取目标资源,无需与服务器做任何通讯。
基于Expires字段实现的强缓存
在以前,我们通常会使用响应头的Expires
字段去实现强缓存。如下图↓
Expires
字段的作用是,设定一个强缓存时间。在此时间范围内,则从内存(或磁盘)中读取缓存返回。
比如说将某一资源设置响应头为:Expires:new Date(“2022-7-30 23:59:59”);
那么,该资源在2022-7-30 23:59:59 之前,都会去本地的磁盘(或内存)中读取,不会去服务器请求。
但是,Expires
已经被废弃了。对于强缓存来说,Expires
已经不是实现强缓存的首选。
因为Expires判断强缓存是否过期的机制是:获取本地时间戳,并对先前拿到的资源文件中的Expires
字段的时间做比较。来判断是否需要对服务器发起请求。这里有一个巨大的漏洞:“如果我本地时间不准咋办?”
是的,Expires
过度依赖本地时间,如果本地与服务器时间不同步,就会出现资源无法被缓存或者资源永远被缓存的情况。所以,Expires
字段几乎不被使用了。现在的项目中,我们并不推荐使用Expires
,强缓存功能通常使用cache-control
字段来代替Expires
字段。
没想到吧,整半天,这个属性是废的。
基于Cache-control实现的强缓存(代替Expires的强缓存实现方法)
Cache-control
这个字段在http1.1中被增加,Cache-control
完美解决了Expires
本地时间和服务器时间不同步的问题。是当下的项目中实现强缓存的最常规方法。
Cache-control
的使用方法页很简单,只要在资源的响应头上写上需要缓存多久就好了,单位是秒。比如↓
//往响应头中写入需要缓存的时间
res.writeHead(200,{
'Cache-Control':'max-age=10'
});
复制代码
下图的意思就是,从该资源第一次返回的时候开始,往后的10秒钟内如果该资源被再次请求,则从缓存中读取。
Cache-Control:max-age=N,N就是需要缓存的秒数。从第一次请求资源的时候开始,往后N秒内,资源若再次请求,则直接从磁盘(或内存中读取),不与服务器做任何交互。
Cache-control
中因为max-age后面的值是一个滑动时间,从服务器第一次返回该资源时开始倒计时。所以也就不需要比对客户端和服务端的时间,解决了Expires
所存在的巨大漏洞。
Cache-control
有max-age、s-maxage、no-cache、no-store、private、public这六个属性。
- max-age决定客户端资源被缓存多久。
- s-maxage决定代理服务器缓存的时长。
- no-cache表示是强制进行协商缓存。
- no-store是表示禁止任何缓存策略。
- public表示资源即可以被浏览器缓存也可以被代理服务器缓存。
- private表示资源只能被浏览器缓存。
no-cache和no-store
no_cache是Cache-control
的一个属性。它并不像字面意思一样禁止缓存,实际上,no-cache的意思是强制进行协商缓存。如果某一资源的Cache-control
中设置了no-cache,那么该资源会直接跳过强缓存的校验,直接去服务器进行协商缓存。而no-store就是禁止所有的缓存策略了。
注意,no-cache和no-store是一组互斥属性,这两个属性不能同时出现在
Cache-Control
中。
public和private
一般请求是从客户端直接发送到服务端,如下↓
但有些情况下是例外的:比如,出现代理服务器,如下↓
而public和private就是决定资源是否可以在代理服务器进行缓存的属性。
其中,public表示资源在客户端和代理服务器都可以被缓存。
private则表示资源只能在客户端被缓存,拒绝资源在代理服务器缓存。
如果这两个属性值都没有被设置,则默认为private
注意,public和private也是一组互斥属性。他们两个不能同时出现在响应头的
cache-control
字段中。
max-age和s-maxage
max-age表示的时间资源在客户端缓存的时长,而s-maxage表示的是资源在代理服务器可以缓存的时长。
在一般的项目架构中max-age就够用。
而s-maxage因为是代理服务端的缓存时长,他必须和上面说的public属性一起使用(public属性表示资源可以在代理服务器中缓存)。
注意,max-age和s-maxage并不互斥。他们可以一起使用。
那么,Cache-control如何设置多个值呢?用逗号分割,如下↓
Cache-control:max-age=10000,s-maxage=200000,public
强制缓存就是以上这两种方法了。现在我们回过头来聊聊,Expires
难道就一点用都没有了吗?也不是,虽然Cache-control是Expires
的完全替代品,但是如果要考虑向下兼容的话,在Cache-control
不支持的时候,还是要使用Expires
,这也是我们当前使用的这个属性的唯一理由。
协商缓存
温馨提示:协商缓存的内容会有一点点绕。需要仔细阅读。
基于last-modified的协商缓存
基于last-modified的协商缓存实现方式是:
- 首先需要在服务器端读出文件修改时间,
- 将读出来的修改时间赋给响应头的
last-modified
字段。 - 最后设置
Cache-control:no-cache
三步缺一不可。
如下图↓
注意圈出来的三行。
第一行,读出修改时间。
第二行,给该资源响应头的last-modified
字段赋值修改时间
第三行,给该资源响应头的Cache-Control
字段值设置为:no-cache.(上文有介绍,Cache-control:no-cache的意思是跳过强缓存校验,直接进行协商缓存。)
还没完。到这里还无法实现协商缓存
当客户端读取到last-modified
的时候,会在下次的请求标头中携带一个字段:If-Modified-Since
。
而这个请求头中的If-Modified-Since
就是服务器第一次修改时候给他的时间,也就是上图中的
这一行。
那么之后每次对该资源的请求,都会带上If-Modified-Since
这个字段,而务端就需要拿到这个时间并再次读取该资源的修改时间,让他们两个做一个比对来决定是读取缓存还是返回新的资源。
如图↓
这样,就是协商缓存的所有操作了。
看到这里,有些小伙伴可能有些迷糊了。
没关系,我们用一张图来解释下协商缓存。
使用以上方式的协商缓存已经存在两个非常明显的漏洞。这两个漏洞都是基于文件是通过比较修改时间来判断是否更改而产生的。
1.因为是更具文件修改时间来判断的,所以,在文件内容本身不修改的情况下,依然有可能更新文件修改时间(比如修改文件名再改回来),这样,就有可能文件内容明明没有修改,但是缓存依然失效了。
2.当文件在极短时间内完成修改的时候(比如几百毫秒)。因为文件修改时间记录的最小单位是秒,所以,如果文件在几百毫秒内完成修改的话,文件修改时间不会改变,这样,即使文件内容修改了,依然不会 返回新的文件。
为了解决上述的这两个问题。从http1.1
开始新增了一个头信息,ETag
(Entity 实体标签)
又来新东西了,兄弟们顶住
基础ETag的协商缓存
不用太担心,如果你已经理解了上面比较时间戳形式的协商缓存的话,ETag
对你来说不会有难度。
ETag
就是将原先协商缓存的比较时间戳的形式修改成了比较文件指纹。
文件指纹:根据文件内容计算出的唯一哈希值。文件内容一旦改变则指纹改变。
我们来看一下流程↓
1.第一次请求某资源的时候,服务端读取文件并计算出文件指纹,将文件指纹放在响应头的etag
字段中跟资源一起返回给客户端。
2.第二次请求某资源的时候,客户端自动从缓存中读取出上一次服务端返回的ETag
也就是文件指纹。并赋给请求头的if-None-Match
字段,让上一次的文件指纹跟随请求一起回到服务端。
3.服务端拿到请求头中的is-None-Match
字段值(也就是上一次的文件指纹),并再次读取目标资源并生成文件指纹,两个指纹做对比。如果两个文件指纹完全吻合,说明文件没有被改变,则直接返回304状态码和一个空的响应体并return。如果两个文件指纹不吻合,则说明文件被更改,那么将新的文件指纹重新存储到响应头的ETag
中并返回给客户端
ajax
1)ajax请求的原理/ 手写一个ajax请求?
2)readyState?
3)ajax异步与同步的区别?
4)ajax传递中文用什么方法?
生成文件指纹,两个指纹做对比。如果两个文件指纹完全吻合,说明文件没有被改变,则直接返回304状态码和一个空的响应体并return。如果两个文件指纹不吻合,则说明文件被更改,那么将新的文件指纹重新存储到响应头的ETag
中并返回给客户端
ajax
1)ajax请求的原理/ 手写一个ajax请求?
2)readyState?
3)ajax异步与同步的区别?
4)ajax传递中文用什么方法?
[外链图片转存中…(img-e9PgRA0p-1714519044912)]
[外链图片转存中…(img-YVt4LWcR-1714519044913)]