面试题目:
答案解析:
浏览器缓存是性能优化中简单高效的一种方式,按照缓存位置划分为以下几种类型:
- service Worker
- Memory Cache
- Disk Cache
- Push Cache
浏览器请求时,会按照如上的优先级顺序,进行查找缓存,都没有命中时,才会去请求网络。
面试过程中问到的浏览器缓存相关问题,一般是针对Disk Cache
而言的,也就是硬盘缓存,下面我们主要对硬盘缓存进行详细解析下。
硬盘缓存分为以下两种类型:
- 强缓存
- 协商缓存
强缓存
强缓存特点是:不需要向服务器发出请求,直接从硬盘缓存中读取想要的资源。在Chrome调试面板 Network面板中,能看到该类请求会返回200状态码,Size
字段显示 from disk cache
,如下图所示:
当我们自己打开一个web页面查看的时候,会发现第一次打开一般都是请求的服务器,然后点击刷新按钮,刷新当前页面,发现 Size
字段并没有出现from disk cache
,而是显示from memory cache
,这是为什么呢?
这是因为浏览器请求资源时,命中了memory cache
,也就是所谓的内存缓存,内存缓存的优先级高于硬盘缓存,也比硬盘缓存的速度快。
如何复现出 disk cache
呢?
重新打开一个浏览器的tab栏,输入页面地址打开,这个时候就会看到 disk cache
了。原因是每个tab栏都会启动新的进程,所以就不会共享内存,也就不会命中memory cache
了。
强缓存有两个头字段控制:Expired
和 Cache-Control
Expired
比如Expires:Thu,31 Dec 2037 23:59:59 GMT
。这个时间代表着这个资源的失效时间,也就是说在2037年12月31日23点59分59秒之前都是有效的,即命中缓存。这种方式有一个明显的缺点,由于失效时间是一个绝对时间,所以当客户端本地时间被修改以后,服务器与客户端时间偏差变大以后,就会导致缓存混乱。于是发展出了Cache-Control。
Cache-Control
Cache-Control
是一个相对时间,例如Cache-Control:3600
,代表着资源的有效期是3600秒。由于是相对时间,并且都是与客户端时间比较,所以服务器与客户端时间偏差也不会导致问题。
Cache-Control
与Expires
可以在服务端配置同时启用或者启用任意一个,同时启用的时候Cache-Control
优先级高。
Cache-Control 可以由多个字段组合而成,主要有以下几个取值:
- max-age 指定一个时间长度,在这个时间段内缓存是有效的,单位是s。例如设置 Cache-Control:max-age=31536000,也就是说缓存有效期为(31536000 / 24 / 60 * 60)天,第一次访问这个资源的时候,服务器端也返回了 Expires 字段,并且过期时间是一年后。在没有禁用缓存并且没有超过有效时间的情况下,再次访问这个资源就命中了缓存,不会向服务器请求资源而是直接从浏览器缓存中取。
-
s-maxage 同 max-age,覆盖 max-age、Expires,但仅适用于共享缓存,在私有缓存中被忽略。
-
public 表明响应可以被任何对象(发送请求的客户端、代理服务器等等)缓存。
-
private 表明响应只能被单个用户(可能是操作系统用户、浏览器用户)缓存,是非共享的,不能被代理服务器缓存。
-
no-cache 强制所有缓存了该响应的用户,在使用已缓存的数据前,发送带验证器的请求到服务器。不是字面意思上的不缓存。
-
no-store 禁止缓存,每次请求都要向服务器重新获取数据。
-
must-revalidate指定如果页面是过期的,则去服务器进行获取。这个指令并不常用,就不做过多的讨论了。
协商缓存
Last-Modified/If-Modified-Since
浏览器第一次请求一个资源的时候,服务器返回的header中会加上Last-Modify,Last-Modified
是一个时间标识该资源的最后修改时间,例如Last-Modified: Thu,31 Dec 2037 23:59:59 GMT
。
当浏览器再次请求该资源时,发送的请求头中会包含If-Modified-Since
,该值为缓存之前返回的Last-Modified
。服务器收到If-Modified-Since
后,根据资源的最后修改时间判断是否命中缓存。
ETag/If-None-Match
与Last-Modified/If-Modified-Since
不同的是,Etag/If-None-Match
返回的是一个校验码(ETag: entity tag
)。ETag
可以保证每一个资源是唯一的,资源变化都会导致ETag变化。ETag
值的变更则说明资源状态已经被修改。服务器根据浏览器上发送的If-None-Match
值来判断是否命中缓存。
既然last-modify
可以识别出资源是否更新,为什么还需要ETag
呢?
-
Last-Modified
标注的最后修改只能精确到秒级,如果某些文件在1秒钟以内,被修改多次的话,它将不能准确标注文件的修改时间 -
如果某些文件会被定期生成,但内容并没有任何变化,
Last-Modified
却改变了,导致该文件被认为更新了,无法使用缓存。
由于ETag
是服务器生成的唯一标识符,能够更加准确的控制缓存,所以ETag
的优先级高于last-Modified
,
总结
浏览器请求时的大致过程描述如下:
- 浏览器发出http请求前,先根据
Cache-Control
和Expired
字段判断缓存是否过期(两个字段同时存在的情况下,根据Cache-Control
判断,该字段不存在时根据Expired
判断)。如果没有过期,浏览器不发出http请求,直接从硬盘缓存中读取资源;如果已过期,则进行第2步逻辑; - 向服务器发出http请求,其中request headers上会携带
If-none-match
和If-Modified-since
字段,服务器收到请求后,会将If-none-match
字段和本地的资源的ETag
进行比较,如果二者不相等,则资源已更新,会返回200
状态码和资源数据,响应头也会带上最新的资源ETag
;如果二者相等,则命中缓存,返回304
,浏览器收到响应后会读取本地缓存资源。如果If-none-match
字段不存在,服务器会根据if-Modified-since
字段进行比对资源的最后修改时间,处理逻辑基本和If-none-match
一致。
补充
浏览器缓存行为还有用户的行为有关!!!
用户操作 | Expires/Cache-Control | Expires/Cache-Control |
---|---|---|
地址栏回车 | 有效 | 有效 |
页面链接跳转 | 有效 | 有效 |
新开窗口 | 有效 | 有效 |
前进、后退 | 有效 | 有效 |
F5刷新 | 无效 | 有效 |
Ctrl+F5刷新 | 无效 | 无效 |
扫一扫 关注我的公众号【前端名狮】,更多精彩内容陪伴你!