浏览器的强缓存和协商缓存
1. 缓存的到底是什么?
众所周知,前端打包后就是生产一些静态文件,然后将其丢到静态服务器上,当有人访问时就下载对应的静态资源就行,为了用户体验,我们可以将静态资源放在缓存中。
而缓存则是指在浏览器中缓存的静态资源,因为静态资源改动情况少,所以比较适合做缓存。
2. 缓存的优缺点?
- 缓存的优点
- 减少重复的数据请求,避免重复的数据请求
- 降低服务器的压力,提升网站性能
- 加快客户端加载网页的速度,提升用户体验
- 缓存的缺点
- 资源如果有更新但是客户端没有及时更新的话会造成数据信息滞后
3. 什么时候用缓存?
我们知道 http 缓存机制主要是在响应头中设定的,响应头中可以设置 Expires、Cache-Control、Last-Modified、Etag 等字段。 其中前两个是用于强缓存,后两个是用于协商缓存的。
我们可以模拟一次缓存的过程。
当我们第一次请求时
当第二次请求时
4. 缓存的类别
1. 强缓存
强缓存不会向服务器发送任何请求,直接从本地缓存中读取静态文件并返回 200 状态码。
上面两种情况都不会访问服务器,而是直接从本地缓存中读取静态文件,当然 from disk 关闭浏览器后依旧存在,而 from memory 关闭浏览器后数据不存在,需要重新获取。
读取策略是优先从 memory 中读取,其次是从 disk 中读取,最后再是从服务器中读取。
而存储策略可能是
- 大文件大概率不存储在内存中,反之优先
- 当前系统内存使用率高的话,文件优先存储进 disk
强缓存的两种设置类型
Expires
Expires: Wed, 22 Mar 2024 08:41:00 GMT
Expires
是 HTTP/1 的产物,表示资源会在 2024 年 3 月 22 日 08:41:00 GMT 后过期,需要再次请求, 同时由于 Expires 使用的GMT
的时间,所以会受限制与本地时间,如果修改了本地时间,可能会造成缓存失效。Cache-Control
Cache-control: max-age=30
Cache-Control
是 HTTP/1.1 的产物,优先级高于Etag
。上面属性表示资源会在 30s 后过期,需要重新请求。
Cache-Control 的指令使用情况,我们可以结合多个指令共同使用,达到客户端和代理服务器都能缓存以及设置失效时间等。
2. 协商缓存
协商缓存会在缓存过期后发起请求验证资源是否有更新,它通过设置 Last-Modified 和 Etag 来实现。
当浏览器发起验证时,如果资源没有更新,那么就会命中协商缓存,返回 304 状态码,并且重新更新浏览器缓存有效期。
Last-Modified 表示文件最后修改日期,If-Modified-Since 会将服务器设置的 Last-Modified 的值发送给服务器,询问服务器在该日期后是否有更新,有更新的话讲新资源发送给客户端,否则返回 304。
但是 Last-Modified 也有一些缺点
- 打开缓存文件即使没有修改也会造成 Last-Modified 被修改,导致服务器不能命中相同的缓存文件
- 因为 Last-Modified 以秒计时,所以如果在不可感知的时间内修改文件会造成文件修改后也会命中缓存,造成数据滞后问题(
Last-Modified
是由一个unix timestamp
表示)
所以就有 Etag 的出场。
Etag 类似于文件指纹,每一个文件都有自己唯一的值,当客户端请求后服务器会将 Etag 值发送给客户端,在下次请求时客户端会带上 If-None-Match 值来与 Etag 值进行比较,如果相同表示资源没有更新,返回 304。并且 Etag 的优先级比 Last-Modified 高。
如何生成 Etag 值呢?
Nginx
中 Etag
由响应头的 Last-Modified
与 Content-Lengt
h 表示为十六进制组合而成。
最后如果什么缓存策略都没设置,那么浏览器会怎么处理?
对于这种情况,浏览器会采用一个启发式的算法,通常会取响应头中的 Date
减去 Last-Modified
值的 10% 作为缓存时间。
5. 缓存场景
对于频繁变动的资源,首先需要使用
Cache-Control: no-cache
使浏览器每次都请求服务器,然后配合 ETag 或者 Last-Modified 来验证资源是否有效。这样的做法虽然不能节省请求数量,但是能显著减少响应数据大小。代码文件可以设置长一点的缓存时间,因为现在打包工具都会对文件名生成 hash 名,所以当 HTML 中引入的文件名修改后才会下载最新到的代码文件,否则就使用缓存。