浏览器缓存

98 阅读9分钟

浏览器缓存机制

什么是浏览器缓存

  • 浏览器会将请求后的资源进行存放为离线资源,当下次需要该资源时,浏览器会根据缓存机制决定直接使用缓存资源还是再次向服务器发送请求

  • 简单来说,浏览器缓存就是把一个已经请求过的Web资源(如html页面,图片,js,数据等)拷贝一份副本储存在浏览器中。缓存会根据进来的请求保存输出内容的副本。

作用

  • 减少了不必要数据的传输,降低服务器的压力
  • 加快了客户端访问速度
  • 增强用户体验

浏览器和网站服务器是如何标识网站页面是否更新的机制?

第一次请求:

image.png

再次请求:

image.png

缓存方式

强制缓存

不向服务器端发送请求,强制使用缓存数据

实现方式

后端在相应头里返回Expires和Cache-Control

Expires

Expires是一个HTTP/1.0的头部字段,它指定了一个日期/时间,在这个日期/时间之后,缓存的资源被认为是过期的。浏览器在下次请求时,会检查缓存资源的Expires值,如果当前时间超过了Expires指定的时间,浏览器会认为该资源已过期,并向服务器发送请求以获取最新的资源。

例如:

Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control

Cache-Control是一个更为强大和灵活的HTTP/1.1头部字段,用于控制缓存行为,约定过期时间的相对时间。它提供了多种指令,允许开发者更精确地定义缓存策略。

Cache-Control的一些常见指令包括:

  • public:指示响应可以被任何缓存存储。
  • private:指示响应只能被单个用户的浏览器缓存存储。
  • no-cache:指示浏览器在每次请求时都必须向服务器验证资源的有效性,即使它在缓存中。
  • no-store:指示浏览器和其他缓存代理不应存储任何版本的响应。
  • max-age:指定资源在缓存中的最大有效时间(以秒为单位)。

例如:

Cache-Control: max-age=3600, public
//这个设置告诉浏览器和缓存代理,该资源在缓存中的有效时间是3600秒,并且可以被任何缓存存储。

注意:当cache-control和expires同时存在时,cache-control的优先级会比expires高

协商缓存

当强缓存失效后,会使用协商缓存,协商缓存由服务器决定是否使用缓存

  1. 向服务器发送请求资源并携带标识

    • Etag字段:表示请求资源在服务器的唯一标识,浏览器可以根据Etag值缓存数据,下次请求的时候以if-None-Matcg字段请求
    • Last-Modified字段:用于标记请求资源的最后一次修改时间
  2. 服务器会进行判断浏览器缓存的资源是否真的失效,是否需要更新;

    • 真的失效,服务器已经更新,返回200,重新返回最新资源和缓存标识。
    • 浏览器再次存入缓存,后续再次从强缓存开始
  3. 缓存时间到了,但是资源没更新,就还是用本地的,直接返回304

缓存位置

四种缓存位置,优先级从下往上(即如果有 Service Worker, 先查询 Service Worker,否则查询 Memory Worker)

  • Sevice Worker:它是使JS运行在主线程之外,虽然自己脱离了浏览器,无法访问dom元素,但是它可以实现离线缓存,消息推送等,其中离线缓存就是指Service Woker Cache,同时它也是PWA实现的重要机制。
  • Memory Cache:它是指内存缓存,它的效率是最快的,但是它的生命周期很短,当渲染进程结束后,它也就不复存在了,常用来存放css,js,image等
  • Disk Cache:它是硬盘缓存,它的存取效率会慢一些,但是它的存储量和存储时长相对比较有优势。
  • Push Cache:推送缓存是Http2中的内容

Service Worker

  1. Service Worker 是运行在浏览器背后的独立线程,一般可以用来实现缓存功能。

  2. 使用Service Worker时,传输协议必须为HTTPS。因为Service Worker中涉及请求拦截,所以必须使用HTTPS协议保障安全。

  3. Service Worker的缓存与浏览器其他内建的缓存机制不同,它可以让我们自由控制缓存那些文件如何匹配缓存,如何读取缓存,并且缓存是持续性的。

  4. Service Worker 实现缓存一般分为三个步骤:

    • 第一步:需要先注册Service Worker
    • 第二步:监听到install事件以后就可以缓存需要的文件,
    • 第三步:用户访问的时候就可以通过拦截请求的方式查询是否存在缓存,存在缓存的话就可以直接读取缓存文件,否则就去请求数据。
  5. 当Service Worker 没有命中缓存的时候,我们需要去调用Fectch函数获取数据。也就是说,如果我们没有在ServiceWorker 命中缓存的话,会根据缓存查找优先级去查找数据。但是不管我们从Memory Cache中还是从网络请求中获取的数据,浏览器都会显示我们是从Service Worker中获取的内容。

Memory Cache

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

那么既然内存缓存这么高效,我们是不是能让数据都存放在内存中呢?这是不可能的。计算机中的内存一定比硬盘容量小很多,操作系统需要精打细算内存的使用,能让我们使用的内存必然不多。每当我们访问页面以后,刷新页面,打开network就可以看到我们很多的数据都是直接来自内存缓存。

内存缓存中有一块重要的缓存资源是preloader的相关指令。(例如)下载的资源。重所周知preloading的相关指令已经是页面优化的常见手段之一,它可以一边解析js/css文件,一边网络请求下一个资源。

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

Disk Cache

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

在所有浏览器缓存中,Disk Cache 覆盖面基本是最大的,它会根据HTTP Header中的字段判断哪些资源需要缓存,哪些资源可以不请求直接使用,哪些资源已经过期需要重新请求。并且即使在跨站点的情况下,相同地址的资源一旦被硬盘存储下来,就不会再次去请求数据。绝大部分的缓存都来自Disk Cache。

浏览器会把哪些文件丢进内存中,哪些丢进硬盘中?关于这点,网上说法不一,不过以下观点比较靠得住:

  • 对于大文件来说,大概率是不存在内存中的,反之优先。
  • 当前系统内存使用率高的话,文件优先储存进硬盘。

一旦关闭标签页,内存中的缓存也就释放了,经过测试:第一次打开www.baidu.com, 大部分资源从服务器中请求

  • 刷新页面,大部分资源从 Memory Cache 或者 Disk Cache 中获取
  • 关闭该标签页,打开www.baidu.com, 大部分资源从Disk Cache中获取。 就像是,在关闭标签页或者浏览器的时候,会将 Memory Cache 中的东西,放到 Disk Cache 中

Push Cache

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

Push Cache在国内能查到的资料很少,因为HTTP/2在国内不够普及 下面列一些结论:

  • 所有资源都能被推送,并且能够被缓存,但是Edge和Safari浏览器智齿相对比较差。
  • 可以推送no-cache和no-store的资源。
  • 一旦链接被关闭,Push Cache就被释放。
  • 多个页面可以使用同一个HTTP/2的连接,也就可以使用同一个Push Cache。这主要还是依赖浏览器的实现而定,出于对性能的考虑,有的浏览器会对相同域名但不同的tab标签使用同一个HTTP连接。

用户行为对浏览器缓存的影响

所谓用户行为对浏览器缓存的影响,指的就是用户在浏览器如何操作时,会触发怎样的缓存策略。主要有三种:

  1. 打开网页,地址栏输入地址,查找disk cache中是否有匹配,如果有则使用,如果没有则发送网络请求。
  2. 普通刷新F5 因为TAB没有关闭,因此memory cache是可用的,会被优先使用,其次才是disk cache。
  3. 强制刷新Ctrl+F5 浏览器不适用缓存,因此发送的请求头均带有Cache-Control:no-Cache 为了兼容还带有Pragma:no-cache,服务器直接返回200和最新内容。