前端 - 浏览器缓存

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/kelly0721/article/details/102548291

总结一下在网上看到的一些浏览器缓存的知识点

一、缓存概述

影响一个网站性能的最直观体验就是网页的打开速度,而提高网页速度的方法之一就是使用缓存。一个优秀的缓存策略可以缩短网页请求资源的距离,减少延迟,并且由于缓存文件可以重复利用,还可以减少带宽,降低网络负荷。

比较常见的就是浏览器会缓存访问过网站的网页,当再次访问这个URL地址的时候,如果网页没有更新,就不会再次下载网页,而是直接使用本地缓存的网页。只有当网站明确标识资源已经更新,浏览器才会再次下载网页。

对于一个数据请求来说,可以分为发起网络请求、后端处理、浏览器响应三个步骤。浏览器缓存可以帮助我们在第一和第三步骤中优化性能。比如说直接使用缓存而不发起请求,或者发起了请求但后端存储的数据和前端一致,那么就没有必要再将数据回传回来,这样就减少了响应数据。

1. web缓存

web缓存是指一个web资源(如html页面,图片,js,数据等)存在于web服务器和客户端(浏览器)之间的副本。

web缓存的作用

  1. 减少网络带宽消耗
  2. 降低服务器压力(可以重复使用本地的缓存,减少对服务器的请求)
  3. 减少网络延迟,加快页面打开速度

web缓存的类型

web缓存大致可以分为以下几种类型:

  1. 数据库数据缓存
  2. 服务器端缓存
  3. 浏览器端缓存
  4. web应用层缓存

前端缓存主要是分为HTTP缓存和浏览器缓存。其中HTTP缓存是在HTTP请求传输时用到的缓存,主要在服务器代码上设置;而浏览器缓存则主要由前端开发在前端js上进行设置。

2. HTTP缓存

概述

HTTP缓存都是从第二次请求开始的。第一次请求资源时,服务器返回资源,并在respone header头中回传资源的缓存参数;第二次请求时,浏览器判断这些请求参数,击中强缓存就直接200,否则就把请求参数加到request header头中传给服务器,看是否击中协商缓存,击中则返回304,否则服务器会返回新的资源。

缓存的位置

从缓存位置上来说分为四种,并且各自有优先级:

  1. Service Worker
  2. Memory Cache
  3. Disk Cache
  4. Push Cache

缓存策略

根据是否需要向服务器重新发起HTTP请求将缓存过程分为两个部分:

  1. 强缓存
  2. 协议缓存

status code 的区别

  • 200 memory : 不访问服务器,关闭浏览器后资源被释放 ,强缓存
  • 200 disk:不访问服务器,关闭浏览器后资源依然存在,强缓存
  • 304 not modify: 访问服务器,发现数据没有更新,服务器返回此状态码。然后从缓存中读取数据。协商缓存200 : 返回资源 size 从服务器端下
  • 载最新资源

3. 浏览器缓存

浏览器缓存即本地存储,主要有以下几种,localStorage,sessionStorage,cookie,和IndexDB

现在缓存的分类有点混乱,有人会分 HTTP缓存和浏览器缓存,也有人分的是浏览器缓存和本地缓存,下文以 浏览器缓存 指代 HTTP缓存
所以,下文主要是 HTTP 缓存

二、缓存的位置

从缓存位置上来说分为四种,并且各自有优先级,当依次查找缓存且都没有命中的时候,才会去请求网络。

  1. Service Worker
    Service Worker 是运行在浏览器背后的独立线程,一般可以用来实现缓存功能。使用 Service Worker的话,传输协议必须为 HTTPS。因为 Service Worker 中涉及到请求拦截,所以必须使用 HTTPS 协议来保障安全。Service Worker 的缓存与浏览器其他内建的缓存机制不同,它可以让我们自由控制缓存哪些文件、如何匹配缓存、如何读取缓存,并且缓存是持续性的。

    当 Service Worker 没有命中缓存的时候,我们需要去调用 fetch 函数获取数据。也就是说,如果我们没有在 Service Worker 命中缓存的话,会根据缓存查找优先级去查找数据。但是不管我们是从 Memory Cache 中还是从网络请求中获取的数据,浏览器都会显示我们是从 Service Worker 中获取的内容。

  2. Memory Cache
    Memory Cache 也就是内存中的缓存,主要包含的是当前中页面中已经抓取到的资源,例如页面上已经下载的样式、脚本、图片等。读取内存中的数据肯定比磁盘快,内存缓存虽然读取高效,可是缓存持续性很短,会随着进程的释放而释放。 一旦我们关闭 Tab 页面,内存中的缓存也就被释放了。

    那么既然内存缓存这么高效,我们是不是能让数据都存放在内存中呢?
    这是不可能的。计算机中的内存一定比硬盘容量小得多,操作系统需要精打细算内存的使用,所以能让我们使用的内存必然不多。

    需要注意的事情是,内存缓存在缓存资源时并不关心返回资源的HTTP缓存头Cache-Control是什么值,同时资源的匹配也并非仅仅是对URL做匹配,还可能会对Content-Type,CORS等其他特征做校验。
    在这里插入图片描述

  3. Disk Cache
    Disk Cache 也就是存储在硬盘中的缓存,读取速度慢点,但是什么都能存储到磁盘中,比之 Memory Cache 胜在容量和存储时效性上。

    在所有浏览器缓存中,Disk Cache 覆盖面基本是最大的。它会根据 HTTP Herder 中的字段判断哪些资源需要缓存,哪些资源可以不请求直接使用,哪些资源已经过期需要重新请求。并且即使在跨站点的情况下,相同地址的资源一旦被硬盘缓存下来,就不会再次去请求数据。绝大部分的缓存都来自 Disk Cache
    以下图片的请求,在另开一个tab页,因为之前已经加载过图片资源,所以直接从 disk cache 中取缓存
    在这里插入图片描述

  4. Push Cache
    Push Cache(推送缓存)是 HTTP/2 中的内容,当以上三种缓存都没有命中时,它才会被使用。它只在会话(Session)中存在,一旦会话结束就被释放,并且缓存时间也很短暂,在Chrome浏览器中只有5分钟左右,同时它也并非严格执行HTTP头中的缓存指令。

    Push Cache 在国内能够查到的资料很少,也是因为 HTTP/2 在国内不够普及。

如果以上四种缓存都没有命中的话,那么只能发起请求来获取资源了。

那么为了性能上的考虑,大部分的接口都应该选择好缓存策略,通常浏览器缓存策略分为两种:强缓存和协商缓存,并且缓存策略都是通过设置 HTTP Header 来实现的。为了方便大家理解,也可以根据是否需要向服务器重新发起HTTP请求将缓存过程分为两个部分。

三、缓存过程分析

在这里插入图片描述

由上图我们可以知道:

  1. 浏览器每次发起请求,都会先在浏览器缓存中查找该请求的结果以及缓存标识

  2. 浏览器每次拿到返回的请求结果都会将该结果和缓存标识存入浏览器缓存中

以上两点结论就是浏览器缓存机制的关键,它确保了每个请求的缓存存入与读取,只要我们再理解浏览器缓存的使用规则,那么所有的问题就迎刃而解了。

对于浏览器端的缓存来讲,缓存规则是在HTTP协议头和HTML页面的 Meta标签中定义的。他们分别从新鲜度和校验值两个维度来规定浏览器是直接使用缓存中的副本,还是需要去源服务器获取更新的版本。

  1. 新鲜度(过期机制):也就是缓存副本有效期。一个缓存副本必须满足以下任一条件,浏览器会认为它是有效的,足够新的,而直接从缓存中获取副本并渲染:

    1. 含有完整的过期时间控制头信息(HTTP协议报头),并且仍在有效期内
    2. 浏览器已经使用过这个缓存副本,并且在一个会话中已经检查过新鲜度
  2. 校验值(验证机制):服务器返回资源的时候有时在控制头信息带上这个资源的实体标签Etag(Entity Tag),它可以用来作为浏览器再次请求过程的校验标识。如过发现校验标识不匹配,说明资源已经被修改或过期,浏览器需求重新获取资源内容。

一个用户发起一个静态资源请求的时候,浏览器会通过以下几步来获取并展示资源:
在这里插入图片描述
缓存行为主要由缓存策略决定,而缓存策略由内容拥有者设置。这些策略主要通过特定的HTTP头部来清晰地表达。

以上过程也可以被概括为三个阶段:

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

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

  3. 缓存失败阶段:当服务器发现请求的资源已经修改过,或者这是一个新的请求(在本来没有找到资源),服务器则返回该资源的数据,并且返回200, 当然这个是指找到资源的情况下,如果服务器上没有这个资源,则返回404。

四、强缓存

不会向服务器发送请求,直接从缓存中读取资源,在chrome控制台的Network选项中可以看到该请求返回200的状态码,并且Size显示from disk cache或from memory cache。强缓存可以通过设置两种 HTTP Header 实现:Expires 和 Cache-Control。

浏览器缓存的控制

  1. 使用HTML的 Meta 标签
    < META HTTP-EQUIV="Pragma" CONTENT="no-cache">
    
    上述代码的作用是告诉浏览器当前页面不被缓存,每次访问都需要去服务器拉取。使用上很简单,但只有部分浏览器可以支持,而且所有缓存代理服务器都不支持,因为代理不解析HTML内容本身。
  2. 使用缓存有关的HTTP消息报头
    一个URI的完整HTTP协议交互过程是由HTTP请求和HTTP响应组成的。
    在HTTP请求和响应的消息报头中,常见的与缓存有关的消息报头有:
    在这里插入图片描述
    接下来详细讲一下

Cache-Control

  1. max-age(单位为s)指定设置缓存最大的有效时间,定义的是时间长短。当浏览器向服务器发送请求后,在max-age这段时间里浏览器就不会再向服务器发送请求了。我们来找个资源看下。比如QQ推广上的css资源,max-age=3600,也就是说缓存有效期为3600秒(也就是1h)。于是在1小时内都会使用这个版本的资源,即使服务器上的资源发生了变化,浏览器也不会得到通知。(只接受 Age 值小于 max-age 值,并且没有过期的对象)
    在这里插入图片描述

  2. max-stale:(可以接受过去的对象,但是过期时间必须小于 max-stale 值)

  3. min-fresh:(接受其新鲜生命期大于其当前 Age 跟 min-fresh 值之和的缓存对象)

  4. s-maxage(单位为s)同max-age,只用于共享缓存(比如CDN缓存)。比如,当s-maxage=60时,在这60秒中,即使更新了CDN的内容,浏览器也不会进行请求。也就是说max-age用于普通缓存,而s-maxage用于代理缓存。如果存在s-maxage,则会覆盖掉max-age和Expires header。

  5. public 指定响应会被缓存,并且在多用户间共享。也就是下图的意思。如果没有指定public还是private,则默认为public。(可以用 Cached 内容回应任何用户)
    在这里插入图片描述

  6. private 响应只作为私有的缓存(见下图),不能在用户间共享。如果要求HTTP认证,响应会自动设置为 private。(只能用缓存内容回应先前请求该内容的那个用户)
    在这里插入图片描述

  7. no-cache 指定不缓存响应,表明资源不进行缓存,但是设置了 no-cache 之后并不代表浏览器不缓存,而是在获取缓存前要向服务器确认资源是否被更改。因此有的时候只设置 no-cache 防止缓存还是不够保险,还可以加上 private 指令,将过期时间设为过去的时间。(可以缓存,但是只有在跟WEB服务器验证了其有效后,才能返回给客户端)

  8. no-store 不允许缓存,每次请求资源都要从服务器重新获取。

  9. must-revalidate 指定如果页面是过期的,则去服务器进行获取。这个指令并不常用,就不做过多的讨论了。

  10. cache-control种类的使用方法
    在这里插入图片描述

  11. 碎碎念,一开始在接口都没看到Cache-Control 这个属性,就很疑惑,查了下资料发现,这个属性要后端有做相应设置才会出现。发一下后端设置这个属性的代码

    Apache
    .htaccess文件
    ```
    <filesMatch "\.(ico|gif|jpg|png|jpeg)$"> 
    ExpiresActive On 
    ExpiresDefault "access plus 11 month" 
    Header append Cache-Control "public" 
    </filesMatch>
    ```
    
    Nginx
    .conf文件
    ```
    location ~* ^.+\.(jpg|jpeg|gif|png|ico)$
    { 
    expires max; 
    }
    ```
    

Expires

缓存过期时间,用来指定资源到期的时间,是服务器端的具体的时间点。也就是说, Expires=max-age + 请求时间 ,需要和Last-modified结合使用。但在上面我们提到过,cache-control的优先级更高。Expires是Web服务器响应消息头字段,在响应http请求时告诉浏览器在过期时间前浏览器可以直接从浏览器缓存取数据,而无需再次请求。
在这里插入图片描述

五、协议缓存

协议缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程,主要有以下两种情况:
1. 协商缓存生效,返回304和Not Modified
2. 协商缓存失效,返回200和请求结果

Last-modified & If-modified-since

服务器端文件的最后修改时间,需要和cache-control共同使用,是检查服务器端资源是否更新的一种方式。当浏览器再次进行请求时,会向服务器传送If-Modified-Since报头,询问Last-Modified时间点之后资源是否被修改过。如果没有修改,则返回码为304,使用缓存;如果修改过,则再次去服务器请求资源,返回码和首次请求相同为200,资源为服务器最新资源。

Etag & & If-None-Match

根据实体内容生成一段hash字符串,标识资源的状态,由服务端产生。浏览器会将这串字符串传回服务器,验证资源是否已经修改,如果没有修改,过程如下:
在这里插入图片描述

六、缓存报头种类与优先级

  1. Cache-Control与Expires
    ache-Control与 Expires的作用一致,都是指明当前资源的有效期,控制浏览器是否直接从浏览器缓存取数据还是重新发请求到服务器取数据。只不过 Cache-Control的选择更多,设置更细致,如果同时设置的话,其优先级高于 Expires。

  2. Last-Modified与ETag
    Etag是服务器自动生成或者由开发者生成的对应资源在服务器端的唯一标识符,能够更加准确的控制缓存。Last-Modified与ETag是可以一起使用的,服务器会优先验证ETag,一致的情况下,才会继续比对Last-Modified,最后才决定是否返回304。

  3. Last-Modified/ETag 与 Cache-Control/Expires
    配置 Last-Modified/ETag的情况下,浏览器再次访问统一URI的资源,还是会发送请求到服务器询问文件是否已经修改,如果没有,服务器会只发送一个304回给浏览器,告诉浏览器直接从自己本地的缓存取数据;如果修改过那就整个数据重新发给浏览器;

    Cache-Control/Expires则不同,如果检测到本地的缓存还是有效的时间范围内,浏览器直接使用本地副本,不会发送任何请求。两者一起使用时, Cache-Control/Expires的优先级要高,即当本地副本根据 Cache-Control/Expires发现还在有效期内时,则不会再次发送请求去服务器询问修改时间 Last-Modified或实体标识 Etag了。

    一般情况下,两者会配合一起使用,因为即使服务器设置缓存时间, 当用户点击“刷新”按钮时,浏览器会忽略缓存继续向服务器发送请求,这时 Last-Modified/ETag将能够很好利用304,从而减少响应开销。

强制缓存优先于协商缓存进行,若强制缓存(Expires和Cache-Control)生效则直接使用缓存,若不生效则进行协商缓存(Last-Modified / If-Modified-Since和Etag / If-None-Match),协商缓存由服务器决定是否使用缓存,若协商缓存失效,那么代表该请求的缓存失效,返回200,重新返回资源和缓存标识,再存入浏览器缓存中;生效则返回304,继续使用缓存。具体流程图如下:
在这里插入图片描述

七、哪些请求不能被缓存?

  1. HTTP信息头中包含Cache-Control:no-cache,pragma:no-cache,或Cache-Control:max-age=0等告诉浏览器不用缓存的请求

  2. 需要根据Cookie,认证信息等决定输入内容的动态请求是不能被缓存的

  3. 经过HTTPS安全加密的请求(有人也经过测试发现,ie其实在头部加入Cache-Control:max-age信息,firefox在头部加入Cache-Control:Public之后,能够对HTTPS的资源进行缓存,参考《HTTPS的七个误解》)

  4. POST请求无法被缓存

  5. HTTP响应头中不包含Last-Modified/Etag,也不包含Cache-Control/Expires的请求无法被缓存

八、用户操作行为与缓存的关系

用户在使用浏览器的时候,会有各种操作,比如输入地址后回车,按F5刷新等,这些行为会对缓存有什么影响呢?
在这里插入图片描述

不同的网页打开方式

  1. 打开新窗口 – 如果指定cache-control的值为private、no-cache、must-revalidate,那么打开新窗口访问时都会重新访问服务器。而如果指定了max-age值,那么在此值内的时间里就不会重新访问服务器,例如:
    Cache-control: max-age=5
    表示当访问此网页后的5秒内再次访问不会去服务器
  2. 在地址栏回车 – 如果值为private或must-revalidate(和网上说的不一样),则只有第一次访问时会访问服务器,以后就不再访问。如果值为no-cache,那么每次都会访问。如果值为max-age,则在过期之前不会重复访问。
  3. 按后退按扭 – 如果值为private、must-revalidate、max-age,则不会重访问,而如果为no-cache,则每次都重复访问
  4. 按刷新按扭 – 无论为何值,都会重复访问

不同的刷新方式

通过上表我们可以看到,当用户在按 F5进行刷新的时候,会忽略Expires/Cache-Control的设置,会再次发送请求去服务器请求,而Last-Modified/Etag还是有效的,服务器会根据情况判断返回304还是200;

而当用户使用 Ctrl+F5进行强制刷新的时候,只是所有的缓存机制都将失效,重新从服务器拉去资源。

  1. 普通刷新 – 当按下F5或者点击刷新按钮来刷新页面的时候,浏览器将绕过本地缓存来发送请求到服务器, 此时, 协商缓存是有效的

  2. 强制刷新 – 当按下ctrl+F5来刷新页面的时候, 浏览器将绕过各种缓存(本地缓存和协商缓存), 直接让服务器返回最新的资源

  3. 回车或转向 – 当在地址栏上输入回车或者按下跳转按钮的时候, 所有缓存都生效

九、如何从缓存角度改善站点

  1. 同一个资源保证URL的稳定性

  2. 给css、js、图片等资源增加HTTP缓存头,并强制入口html不被缓存

  3. 减少对Cookie的依赖

  4. 多用Get方式请求动态Cgi

  5. 动态CGI也是可以被缓存

十、本地缓存

本地存储主要有以下几种,localStorage,sessionStorage,cookie和IndexDB,主要用在前端有大容量存储需求的页面上,例如,在线编辑浏览器或者网页邮箱。他们都可以将数据存储在浏览器,应该根据不同的场景进行使用。

  1. Cookie :
    主要是由服务器生成,且前端也可以设置,保存在客户端本地的一个文件,通过response响应头的set-Cookie字段进行设置,且Cookie的内容自动在请求的时候被传递给服务器。

  2. LocalStorage
    主要是前端开发人员,在前端设置,一旦数据保存在本地后,就可以避免再向服务器请求数据,因此减少不必要的数据请求,减少数据在浏览器和服务器间不必要地来回传递。
    可以长期存储数据,没有时间限制,一天,一年,两年甚至更长,数据都可以使用,一般浏览器支持的是5M大小,这个在不同的浏览器中localStorage会有所不同。
    是一种本地存储的公共资源,可长期且同源域名下资源可共享

  3. SessionStorage
    sessionStorage主要是前端开发人员,在前端设置,sessionStorage(会话存储),只有在浏览器被关闭之前使用,创建另一个页面时同意可以使用,关闭浏览器之后数据就会消失

  4. indexDB
    IndexedDB 就是浏览器提供的本地数据库,它可以被网页脚本创建和操作。IndexedDB 允许储存大量数据,提供查找接口,还能建立索引。这些都是 LocalStorage 所不具备的。就数据库类型而言,IndexedDB 不属于关系型数据库(不支持 SQL 查询语句),更接近 NoSQL 数据库。

参考链接:
https://segmentfault.com/a/1190000017962411?utm_source=tag-newest#articleHeader0
https://www.jianshu.com/p/54cc04190252

猜你喜欢

转载自blog.csdn.net/kelly0721/article/details/102548291