浏览器缓存机制
一、概述
1. 什么是缓存
简单来说,浏览器缓存就是把一个已经请求过的Web资源(如html页面,图片,js,数据等)拷贝一份副本储存在浏览器中。缓存会根据进来的请求保存输出内容的副本。当下一个请求来到的时候,如果是相同的URL,缓存会根据缓存机制决定是直接使用副本响应访问请求,还是向源服务器再次发送请求。比较常见的就是浏览器会缓存访问过网站的网页,当再次访问这个URL地址的时候,如果网页没有更新,就不会再次下载网页,而是直接使用本地缓存的网页。只有当网站明确标识资源已经更新,浏览器才会再次下载网页。
2. 缓存机制
简化流程吐下:
- 浏览器在加载资源时,根据请求头的 expires 和 cache-control 判断是否命中强缓存,是则直 接从缓存读取资源,不会发请求到服务器。
- 如果没有命中强缓存,浏览器一定会发送一个请求到服务器,通过 last-modified 和 etag 验证 资源是否命中协商缓存,如果命中,服务器会将这个请求返回,但是不会返回这个资源的数据,依 然是从缓存中读取资源。
- 如果前面两者都没有命中,直接从服务器加载资源。
3. 缓存的好处
- 减少网络延迟,加快页面打开速度
- 减少网络带宽消耗
- 降低服务器压力
二、强缓存
浏览器缓存一般分为两类:强缓存(也称本地缓存)和协商缓存(也称弱缓存)。
- 强缓存:
Expires
,Cache-Control
- 协商缓存:
Last-Modified
/If-Modified-Since
和Etag
/If-None-Match
强制缓存优先于协商缓存进行,若强制缓存(Expires和Cache-Control)生效则直接使用缓存,若不生效则进行协商缓存(Last-Modified / If-Modified-Since和Etag / If-None-Match),协商缓存由服务器决定是否使用缓存,若协商缓存失效,那么代表该请求的缓存失效,重新获取请求结果,再存入浏览器缓存中;生效则返回304,继续使用缓存,主要过程如下:
1. Expires
Expires是HTTP/1.0
控制网页缓存的字段,其值为服务器返回该请求结果缓存的到期时间,即再次发起该请求时,如果客户端的时间小于Expires的值时,直接使用缓存结果。
Expires: Wed, 22 Nov 2019 08:41:00 GMT
表示资源在2019年11月22号8点41分过期,过期了就得向服务端发请求。
到了HTTP/1.1,Expire已经被Cache-Control替代,原因在于Expires控制缓存的原理是使用客户端的时间与服务端返回的时间做对比,那么如果客户端与服务端的时间因为某些原因(例如时区不同;客户端和服务端有一方的时间不准确)发生误差,那么强制缓存则会直接失效,这样的话强制缓存的存在则毫无意义,那么Cache-Control又是如何控制的呢?
2. Cache-Control
在HTTP/1.1
中,Cache-Control
是最重要的规则,主要用于控制网页缓存,主要取值为:
public
:所有内容都将被缓存(客户端和代理服务器都可缓存)private
:所有内容只有客户端可以缓存,Cache-Control
的默认取值no-cache
:客户端缓存内容,但是是否使用缓存则需要经过协商缓存来验证决定no-store
:所有内容都不会被缓存,即不使用强制缓存,也不使用协商缓存max-age=xxx
(xxx is numeric):缓存内容将在xxx秒后失效
需要注意,Cache-Control
优先级比Expires
优先级高,当Expires
和Cache-Control
同时存在的时候,Cache-Control
会优先考虑。
三、协商缓存
强缓存失效之后,浏览器在请求头中携带相应的缓存tag来向服务器发请求,由服务器根据这个tag,来决定是否使用缓存,这就是协商缓存。
1. Last-Modified / If-Modified-Since
Last-Modified
是服务器响应请求时,返回该资源文件在服务器最后被修改的时间
If-Modified-Since
则是客户端再次发起该请求时,携带上次请求返回的Last-Modified
值,通过此字段值告诉服务器该资源上次请求返回的最后被修改时间。服务器收到该请求,发现请求头含有If-Modified-Since
字段,则会根据If-Modified-Since
的字段值与该资源在服务器的最后被修改时间做对比,若服务器的资源最后被修改时间大于If-Modified-Since
的字段值,则重新返回资源,状态码为200;否则则返回304,代表资源无更新,可继续使用缓存文件
2. Etag / If-None-Match
Etag
是服务器响应请求时,返回当前资源文件的一个唯一标识(由服务器生成)
If-None-Match
是客户端再次发起该请求时,携带上次请求返回的唯一标识Etag
值,通过此字段值告诉服务器该资源上次请求返回的唯一标识值。服务器收到该请求后,发现该请求头中含有If-None-Match
,则会根据If-None-Match
的字段值与该资源在服务器的Etag值做对比,一致则返回304,代表资源无更新,继续使用缓存文件;不一致则重新返回资源文件,状态码为200
注:Etag / If-None-Match
优先级高于Last-Modified / If-Modified-Since
,同时存在则只有Etag / If-None-Match
生效。
3. 两者对比
在精准度上,
ETag
优于Last-Modified
。优于ETag
是按照内容给资源上标识,因此能准确感知资源的变化。而Last-Modified
就不一样了,它在一些特殊的情况并不能准确感知资源变化,主要有两种情况:- 编辑了资源文件,但是文件内容并没有更改,这样也会造成缓存失效。
Last-Modified
能够感知的单位时间是秒,如果文件在 1 秒内改变了多次,那么这时候的Last-Modified
并没有体现出修改了。
在性能上,
Last-Modified
优于ETag
,也很简单理解,Last-Modified
仅仅只是记录一个时间点,而Etag
需要根据文件的具体内容生成哈希值。
4. 浏览器HTTP请求过程
第一次请求
再次请求
四、缓存位置
命中缓存之后,我们需要在缓存中提取数据,缓存位置如下从高到低:
Memory Cache
(内存缓存)Service Worker
(离线缓存)Disk Cache
(磁盘缓存)Push Cache
(推送缓存)—— HTTP2 新特性
1. Memory Cache
Memory Cache
指的是内存缓存,从效率上讲它是最快的。但是从存活时间来讲又是最短的,当渲染进程结束后,内存缓存也就不存在了。因为内存空间比较小,通常较小的资源放在内存缓存中,比如 base64
图片等资源
2. Disk Cache
Disk Cache
就是存储在磁盘中的缓存,从存取效率上讲是比内存缓存慢的,但是他的优势在于存储容量和存储时长。稍微有些计算机基础的应该很好理解,就不展开了。
浏览器如何决定将资源放进内存还是硬盘呢?主要策略如下:
- 比较大的JS、CSS文件会直接被丢进磁盘,反之丢进内存
- 内存使用率比较高的时候,文件优先进入磁盘
3. Service Worker
Service Worker
是一种独立于主线程之外的 Javascript
线程。它脱离于浏览器窗体,因此无法直 接访问 DOM 。 可以帮我们实现离线缓存、消息推送和网络代理等功能
4. Push Cache
即推送缓存,这是浏览器缓存的最后一道防线。它是 HTTP/2
中的内容,虽然现在应用的并不广泛,但随着 HTTP/2 的推广,它的应用越来越广泛。
五、用户行为对浏览器缓存的影响
- 打开网页,地址栏输入地址: 查找
disk cache
中是否有匹配。如有则使用;如没有则发送网络请求。 - 普通刷新 (F5):因为 TAB 并没有关闭,因此
memory cache
是可用的,会被优先使用(如果匹配的话)。其次才是disk cache
。 - 强制刷新 (Ctrl + F5):浏览器不使用缓存,因此发送的请求头部均带有
Cache-control:no-cache
(为了兼容,还带了Pragma:no-cache
),服务器直接返回 200 和最新内容。