5

一文搞定浏览器缓存

 3 years ago
source link: https://zhuanlan.zhihu.com/p/353310851
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

一文搞定浏览器缓存

北京奇观技术有限责任公司 软件开发工程师
  • 浏览器缓存机制
  • 强制缓存
  • 协商缓存
  • Service Worker
  • Memory Cache
  • Disk Cache
  • Push Cache
  • 用户行为对缓存的影响
  • APP三级缓存和浏览器缓存的关系

浏览器缓存机制

机制:浏览器在发送请求时,首先会检查强制缓存,如果缓存命中,则不需要发送请求。直接从缓存中获取资源数据,若强缓存失效,则发送请求进去协商缓存,服务器通过浏览器请求头Last-Modified和Etag字段进行检查,若是资源则返回新的资源数据,200状态码,否则返回304。

现象:当服务器返回304时说明页面使用了这个资源的缓存。如果客户端发送了一个带条件的GET请求且该请求已被允许,而文档的内容(从上次访问以来或是根据请求条件)并没有发生变化,则服务器应该返回这个304状态码。简单表达就是:服务端已经执行了GET,但文件并没有发生变化。

浏览器在发起http请求前首先检查的就是强制缓存,也就是强制缓存并不需要去发送请求,只需要通过携带的字段确认实现,在早起的http1.0中是通过Expires字段,后来的http1.1中使用Cache-Control字段。

  • 1 HTTP1.0

HTTP1.0使用Expires字段。Expires是指过期时间,他存在服务器的相应头中,用来告诉浏览器在这个过期时间之前是不需要请求的,可以使用缓存内容,但是这个额实现机制中存在一个坑点就是服务器的时间和浏览器的时间可能不一致,这样就会导致过期时间不准备,所以http1.1就不用这个字段了,是用来Cache-control字段 。

  • HTTP1.1

Cache-Control和 Expires 最大的区别是它不是采取过期时间点,而是利用过期时间,通过max-age属性来传达过期时长,如Cache-Control:max-age:3600, 就表示在一个小时内都可以使用缓存内容。

Cache-Control的其他属性:

  • public:表示客服端和代理服务器都可以进行缓存
  • private:表示只能客户端缓存
  • no-cache:表示跳过当前的强制缓存,发起http请求,直接进入协商缓存
  • no-store:表示不进行缓存
  • s-maxage: 表示针对代理服务器的缓时间

如果强制缓存失效了,则会发起请求,进入到协商缓存

在强制缓存失效后,浏览器会在请求头中携带缓存tag向服务器发起请求,服务器根据该tag值去判断是否使用缓存,其中tag字段分别为Last-Modified和Etag。

  • Last-modified (Http1.0时是用这个)

表示最后修改时间,浏览器向服务器发起请求后,服务器会在响应头中加上该字段,在浏览器下一次发送请求时,会在请求头中携带If-Modified-Since字段,该字段的值就是服务器传来的最后一次修改时间,服务器拿到这个字段值后就会和自身最后修改时间进行对比,如果两者不一样,就说明更新了,返回200,否则返回304告诉浏览器使用缓存的数据

Last-modified -> if-Modified-Since

  • Etag (Http1.1时出的这个)

表示服务器根据当前文件资源生成的唯一标识,服务器在响应头中将该字段传递给浏览器,浏览器在下次请求时会将这个值作为if-None-match字段的内容并放到请求头中,服务器接收到if-None-Match后会跟服务器上的Etag进行对比,如果两者不一样,说明更新了,返回200,否则返回304,告诉浏览器使用缓存内容。

Etag ->if-None-Match

  • 二者对比:

在精度上Etag是优于Last-modified的,因为Etag是根据资源文件内容生成的唯一标示,因为能够准确感知到资源文件内容的变化,而Last-modified是通过时间点来感知,可能1s内修改多次,此时就不能体现修改。在性能上Last-modified是优于Etag的,Last-Modified仅仅只是记录一个时间点,而Etag需要根据文件的具体内容生成哈希值。

浏览器缓存位置共有四种,并且各自有优先级,当依次查找缓存且都没有命中的时候,才会去请求网络。

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

Service Worker

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

Service Worker 实现缓存一般分为三个步骤:首先需要先注册Service Worker,然后监听到install事件以后就可以缓存需要的文件,那么下次用户访问的时候就可以通过拦截请求的方式查询是否存在缓存,存在缓存的话就可以直接读取缓存文件,否则就去请求数据。

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

Memory Cache

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

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

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

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

Disk Cache

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

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

浏览器会把哪些文件丢进内存中,哪些丢进硬盘中?关于这点,网上说法不一,不过以下观点比较靠得住:1 对于大文件来说,大概率是不存在内存中的,反之优先。2当前系统内存使用率高的话,文件优先储存进硬盘。

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和最新内容。

APP三级缓存和浏览器缓存的关系

什么是APP三级缓存

  1. 网络缓存:不优先加载,速度慢,浪费流量
  2. 本地缓存:次优先加载,速度快
  3. 内存缓存,优先加载,速度最快

首次加载Android App时,肯定要通过网络交互来获取图片,之后我们可以将图片保存至本地SD卡和内存中。之后运行App时,优先访问内存中的图片缓存,若内容中没有,则加载SD卡中的图片。总之只有初次访问新内容时,才通过网络获取图片资源。

根据上面对浏览器缓存的描述,其实APP三级缓存等于浏览器缓存,并不是等于三级缓存中的网络缓存。

(这是源于一次面试,当时我并不知道APP三级缓存,面试官是做APP的不知道浏览器缓存,当我讲完浏览器缓存后,他说这只是APP三级缓存中的一级,回去后查阅资料以及跟公司的安卓讨论后觉得其实APP的三级缓存就等于浏览器缓存。)


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK