缓存
http 缓存
- 浏览器缓存即
http
缓存,将数据缓存在浏览器(即客户端)。 - 服务端通过设置
HTTP
响应头来决定缓存策略,将资源缓存到本地浏览器。
Web
缓存的作用
- 提高首屏加载速度->优化用户体验
- 减少流量消耗
- 减轻服务器压力
工作流程
- 第一次请求资源,服务器返回对应资源,并在
response header
响应头中添加缓存策略。 - 第二次请求时,浏览器判断请求参数,命中强缓存就直接
200
,从本地缓存中拿数据。否则把响应参数存在request header
请求头中,看是否命中协商缓存,命中则返回304
,否则服务器会返回全新资源。
强缓存
- 直接拿本地副本对比读取,不读服务器,返回状态码是
200
- 使用定时器的方式,让强缓存设置静态资源的有效期,如果超过有效期则认为缓存作废。
- 无需与服务端发生交互
Expires
HTTP/1.0
控制网页缓存的字段,其值为服务器返回该请求的结果缓存的到期时间,即再次发送请求时如果客户端的时间小于 Expires
的值时,直接使用缓存结果。在 HTTP/1.1
,Expires
已经被 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
Pragma
是 HTTP/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-Control | Last-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 只需要以下几个步骤
- 检测是否支持
ServiceWorker
- 注册(register)
- 安装(install)
- 激活(active)
- 使用(activing)
- 卸载(unregister)