Skip to content

缓存

http 缓存

  • 浏览器缓存即 http 缓存,将数据缓存在浏览器(即客户端)。
  • 服务端通过设置 HTTP 响应头来决定缓存策略,将资源缓存到本地浏览器。

Web 缓存的作用

  • 提高首屏加载速度->优化用户体验
  • 减少流量消耗
  • 减轻服务器压力

工作流程

  • 第一次请求资源,服务器返回对应资源,并在 response header 响应头中添加缓存策略。
  • 第二次请求时,浏览器判断请求参数,命中强缓存就直接 200,从本地缓存中拿数据。否则把响应参数存在 request header 请求头中,看是否命中协商缓存,命中则返回 304,否则服务器会返回全新资源。

强缓存

  • 直接拿本地副本对比读取,不读服务器,返回状态码是 200
  • 使用定时器的方式,让强缓存设置静态资源的有效期,如果超过有效期则认为缓存作废。
  • 无需与服务端发生交互

Expires

HTTP/1.0 控制网页缓存的字段,其值为服务器返回该请求的结果缓存的到期时间,即再次发送请求时如果客户端的时间小于 Expires 的值时,直接使用缓存结果。在 HTTP/1.1Expires 已经被 Cache-Control 替代,原因在于Expires 控制缓存的原理是使用客户端的时间与服务端返回的时间做对比,如果时间发生误差,那么强制缓存将直接失效。

Cache-Control

Cache-Control 是最重要的规则,主要用于控制网页缓存。

  • public:所有内容都将被缓存(客户端和代理服务器都可缓存)
  • private:所有内容只有客户端可以缓存,Cache-Control 的默认取值
  • no-cache:客户端缓存内容,但是是否使用缓存则需要经过协商缓存来验证决定
  • no-store:所有内容都不会被缓存,即不使用强制缓存,也不使用协商缓存
  • max-age=xx:缓存在 xxx 秒后失效。
  • must-revalidate:告诉浏览器、缓存服务器,本地副本过期前,可以使用本地副本;本地副本一旦过期,必须去源服务器进行有效性校验。

在无法确定客户端的时间是否与服务端的时间同步的情况下,Cache-Control 相比于 expires 是更好的选择。

Cache-Control 字段是 http 报文中的通用首部字段,既存在于请求报文中,也存在于响应报文中

共有字段:

no-cache:无论缓存是否过期,都要对请求进行校验 no-store:请求报文中可能存在机密信息,不可缓存 (缓存字段中优先级最高) max-age=[秒]:资源x秒后过期,未过期则使用缓存 no-transform:禁止代理改变实体主体的媒体类型,也包括压缩 cache-extension:自定义拓展值,如果缓存服务器不能理解,则忽略

请求报文私有字段值:

max-stale(=[秒]): 提示缓存服务器,即使缓存过期也使用;或者在过期后的指定时间内依然使用缓存 min-fresh(=[秒]): 提示缓存服务器,如果缓存在指定时间内还没过期,则返回 only-if-cache: 提示服务器如果有缓存就返回,不需要确认有效性。如果没有,则返回504网关超时

响应报文私有字段值:

public: 明确指明缓存可以给所有用户使用 private: 明确指明缓存不可以给其他用户使用 must-revalidate: 如果缓存未过期,则返回;否则代理在返回缓存数据之前,必须向源服务器发起请求,验证缓存是否有效。如果无法连接上源服务器,则返回504网关超时 proxy-revalidate: 所有缓存服务器在返回缓存数据前,都要向源服务器发起请求验证有效性 s-maxage=[秒]: 缓存资源的时间小于指定时间时,直接返回缓存

Pragma

PragmaHTTP/1.0 标准中定义的一个 header 属性,请求中包含 Pragma 的效果跟在头信息中定义 Cache-Control: no-cache 相同。但是 HTTP 的响应头没有明确定义这个属性,所以它不能拿来完全替代 HTTP/1.1 中定义的 Cache-control 头。

优先级顺序为:Pragma > Cache-Control > Expires

from memory cache 和 from disk cache

内存缓存(from memory cache):内存缓存具有两个特点,分别是快速读取和时效性:

  • 1、快速读取:内存缓存会将编译解析后的文件,直接存入该进程的内存中,占据该进程一定的内存资源,以方便下次运行使用时的快速读取。
  • 2、时效性:一旦该进程关闭,则该进程的内存则会清空。

一般脚本,字体,图片会存在内存当中

硬盘缓存(from disk cache):

硬盘缓存则是直接将缓存写入硬盘文件中,读取缓存需要对该缓存存放的硬盘文件进行I/O操作,然后重新解析该缓存内容,读取复杂,速度比内存缓存慢。

一般非脚本会存在内存当中,如 CSS

协商缓存

  • 协商缓存指的是浏览器和服务器之间是否要使用缓存在做协商。如果协商的结果是需要更新会返回200并返回更新内容。如果不需要只需要返回状态码304不用返回内容,只是需要后端做应答,但是不需要后端重新生成内容。
  • 协商缓存:需要与服务端发生交互,判断是否使用本地缓存的文件。

Last-modified & if-modified-since

Last-Modified 是 HttpHeader 中的资源的最后修改时间,如果带有 Last-Modified ,下一次发送 Http 请求时,将会发生带 If-modified-since 的 HttpHeader。如果没有过期,将会收到 304 的响应,从缓存中读取。

这是一组通过协商修改时间为基础的策略。

  • 服务器向后端发送一个数据上次被修改的时间标签。
  • 浏览器知道了服务器资源上次修改的时间,后续请求中,会和服务器进行时间的比较,如果服务器上的时间比本地时间要新,说明数据有更改,浏览器需要重新下载数据。

ETag && if-no-match

Etag 是 HttpHeader 中代表资源的标签,在服务器端生成。如果带有 Etag ,下一次发送带 Etag 的请求,如果 Etag 没有变化将收到 304 的响应,从缓存中读取。

Etag 在使用时要注意相同资源多台 Web 服务器的 Etag 的一致性。 通过内容判断,一般的做法是将返回内容进行摘要,然后通过对比来判断内容是否需要更新。

用户操作Expires/Cache-ControlLast-modified/ETag
地址栏回车有效有效
页面链接跳转有效有效
新开窗口有效有效
前进后退有效有效
F5刷新无效有效
强制刷新无效无效

Ajax缓存

定时缓存

将某个接口数据在一定时间段内缓存起来,缓存期内不再发起请求直接返回本地数据,过了这段时间再重新获取并更新缓存。 定时缓存实际上是牺牲了数据实时性换取响应速度,使用中通过设置不同的缓存时长,可以匹配不同的业务场景,比如对于相对稳定的数据可以设置较长的缓存时间,而设置较短的缓存时间则可以起到请求“防抖”作用。

快照缓存

更多的时候我们希望接口能兼具实时性和响应速度,比如应用首屏的异步数据块,既要快又要新,虽然这种需求听起来很“不科学”,但我们确实可以通过“快照缓存”满足这个需求。此时每当接口成功请求后都会为数据建立一份“快照”。下次请求时接口会首先将最近的快照数据作为结果返回,供前端渲染界面,同时发送请求获取最新数据,新数据到达后会与快照做对比,如果与快照相同则缓存命中,如果与快照不同会更新快照,并将新数据返回,供前端更新界面。

ServiceWorker

Service Worker 是HTML5 的一个新特性,主要用来做持久的离线缓存

ServiceWorker 可以干嘛

解放主线程,Web Worker 是脱离在主线程之外的,将一些复杂的耗时的活交给它干,完成后通过 postMessage 方法告诉主线程,而主线程通过 onMessage 方法得到 Web Worker 的结果反馈。

优点

  • 省去建立 tcp 的连接时长,加快首屏加载速度
  • 减少静态资源服务器的负载

功能与特性

  • Service Worker 拥有自己独立的 worker 线程,独立于当前网页线程
  • 离线缓存静态资源
  • 拦截代理请求和响应
  • 可自定义响应内容
  • 可以通过 postMessage 向主线程发送消息
  • 无法直接操作 DOM
  • 必须在 HTTPS 环境下工作,或者本地。
  • 通过 Promise 异步实现
  • Service Worker 安装(installing)完成后,就会一直存在,除非手动卸载(unregister)

生命周期

Service Worker 的生命周期完全独立于网页

  • 注册 (register)
  • 安装 (install)
  • 激活 (activate)

使用 service worker 只需要以下几个步骤

  1. 检测是否支持 ServiceWorker
  2. 注册(register)
  3. 安装(install)
  4. 激活(active)
  5. 使用(activing)
  6. 卸载(unregister)

Released under the MIT License.