前端缓存机制

浏览器的localStorage和sessionStorage等不属于浏览器的缓存概念,准确的说应该属于“浏览器的本地存储”不要讲两者混淆。
从缓存位置上来说一般有以下四种,依次是:
Memory Cache、Service Worker Cache、Disk Cache、Push Cache
前端缓存机制_第1张图片

  • MemoryCache,是指存在内存中的缓存。从优先级上来说,它是浏览器最先尝试去命中的一种缓存。从效率上来说,它是响应速度最快的一种缓存。浏览器秉承的是“节约原则”,我们发现,Base64 格式的图片,几乎永远可以被塞进 memory cache,这可以视作浏览器为节省渲染开销的“自保行为”;此外,体积不大的 JS、CSS 文件,也有较大地被写入内存的几率——相比之下,较大的 JS、CSS 文件就没有这个待遇了,内存资源是有限的,它们往往被直接甩进磁盘。一旦我们关闭 Tab 页面,内存中的缓存也就被释放了。
  • Service Worker, 是一种独立于主线程之外的 Javascript 线程。它脱离于浏览器窗体,因此无法直接访问 DOM。这样独立的个性使得 Service Worker 的“个人行为”无法干扰页面的性能,这个“幕后工作者”可以帮我们实现离线缓存、消息推送和网络代理等功能。我们借助 Service worker 实现的离线缓存就称为 Service Worker Cache。
  • Disk Cache(HTTP Cache),Disk Cache 也就是存储在硬盘中的缓存,读取速度慢点,但是什么都能存储到磁盘中,比之 Memory Cache 胜在容量和存储时效性上。在所有浏览器缓存中,Disk Cache 覆盖面基本是最大的。它会根据 HTTP Header 中的字段判断哪些资源需要缓存,哪些资源可以不请求直接使用,哪些资源已经过期需要重新请求。并且即使在跨站点的情况下,相同地址的资源一旦被硬盘缓存下来,就不会再次去请求数据。绝大部分的缓存都来自 Disk Cache,关于 HTTP 的协议头中的缓存字段,我们会在下文进行详细介绍。它又分为强缓存和协商缓存。
  • Push Cache,Push Cache 是指 HTTP2 在 server push 阶段存在的缓存。
    Push Cache 是缓存的最后一道防线。浏览器只有在 Memory Cache、HTTP Cache 和 Service Worker Cache 均未命中的情况下才会去询问 Push Cache。
    Push Cache(推送缓存) 是一种存在于会话阶段的缓存,当 session 终止时,缓存也随之释放。
    不同的页面只要共享了同一个 HTTP2 连接,那么它们就可以共享同一个 Push Cache。

浏览器缓存的两种方式

本地缓存阶段(也称强缓存):

先在本地查找该资源,如果有发现该资源,而且该资源还没有过期,就使用这一个资源,完全不会发送http请求到服务器;

协商缓存阶段(也称弱缓存):

如果在本地缓存找到对应的资源,但是不知道该资源是否过期或者已经过期,则发一个http请求到服务器,然后服务器判断这个请求,如果请求的资源在服务器上没有改动过,则返回304,让浏览器使用本地找到的那个资源;

Cache-Control

  • max-age(单位为s)指定设置缓存最大的有效时间,定义的是时间长短。当浏览器向服务器发送请求后,在max-age这段时间里浏览器就不会再向服务器发送请求了(强缓存)。
  • s-maxage(单位为s)同max-age,只用于共享缓存(比如CDN缓存)。比如,当s-maxage=60时,在这60秒中,即使更新了CDN的内容,浏览器也不会进行请求。也就是说max-age用于普通缓存,而s-maxage用于代理缓存。如果存在s-maxage,则会覆盖掉max-age和Expires header。
  • no-cache 指定不缓存响应,表明资源不进行缓存,但是设置了 no-cache 之后并不代表浏览器不缓存,而是在获取缓存前要向服务器确认资源是否被更改。因此有的时候只设置 no-cache 防止缓存还是不够保险,还可以加上 private 指令,将过期时间设为过去的时间。(no-cache 绕开了浏览器:我们为资源设置了 no-cache 后,每一次发起请求都不会再去询问浏览器的缓存情况,而是直接向服务端去确认该资源是否过期,即走协商缓存的路线)(协商缓存
  • no-store 绝对禁止缓存,如果用了这个命令就是不会进行缓存,每次请求资源都要从服务器重新获取。(no-store 比较绝情,顾名思义就是不使用任何缓存策略。在 no-cache 的基础上,它连服务端的缓存确认也绕开了,只允许你直接向服务端发送请求、并下载完整的响应。)

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字段。

基于last-modified的协商缓存

基于last-modified的协商缓存实现方式是:

  • 首先需要在服务器端读出文件修改时间,
  • 将读出来的修改时间赋给响应头的last-modified字段。
  • 最后设置Cache-control:no-cache
    三步缺一不可。
    前端缓存机制_第2张图片
    注意圈出来的三行。
    第一行,读出修改时间。
    第二行,给该资源响应头的last-modified字段赋值修改时间
    第三行,给该资源响应头的Cache-Control字段值设置为:no-cache.(上文有介绍,Cache-control:no-cache的意思是跳过强缓存校验,直接进行协商缓存。)
    还没完。到这里还无法实现协商缓存
    当客户端读取到last-modified的时候,会在下次的请求标头中携带一个字段:If-Modified-Since。
    前端缓存机制_第3张图片

基础ETag的协商缓存

ETag就是将原先协商缓存的比较时间戳的形式修改成了比较文件指纹。

文件指纹:根据文件内容计算出的唯一哈希值。文件内容一旦改变则指纹改变。

我们来看一下流程↓

  1. 第一次请求某资源的时候,服务端读取文件并计算出文件指纹,将文件指纹放在响应头的etag字段中跟资源一起返回给客户端。
  2. 第二次请求某资源的时候,客户端自动从缓存中读取出上一次服务端返回的ETag也就是文件指纹。并赋给请求头的if-None-Match字段,让上一次的文件指纹跟随请求一起回到服务端。
  3. 服务端拿到请求头中的is-None-Match字段值(也就是上一次的文件指纹),并再次读取目标资源并生成文件指纹,两个指纹做对比。如果两个文件指纹完全吻合,说明文件没有被改变,则直接返回304状态码和一个空的响应体并return。如果两个文件指纹不吻合,则说明文件被更改,那么将新的文件指纹重新存储到响应头的ETag中并返回给客户端

前端缓存机制_第4张图片本文参考:
https://juejin.cn/post/7127194919235485733
https://www.jianshu.com/p/cb72dfb0f7b3

你可能感兴趣的:(前端,缓存)