面试题 -- 说下浏览器缓存策略

2,755 阅读9分钟

前言

最近面试被问到浏览器缓存策略相关问题,重新梳理一下关于缓存的知识。

浏览器在本地对用户对最近请求过的文档进行存储,当用户再次访问同一页面时,浏览器就可以直接从本地磁盘加载文件。

浏览器缓存的意义

    1. 避免了冗余的数据传输,节省流量;
    1. 加快了用户访问网页的速度;
    1. 减小了服务器的压力;

阅读本文,你将学到:

1. 浏览器缓存位置、缓存类型
2. 缓存优先级
3. 强缓存、协商缓存区别
4. 如何配置缓存策略
5. 实例分析掘金网站缓存策略

一、浏览器缓存资源的位置

浏览器缓存资源的位置主要有两个,内存(from memory cache)和硬盘(from disk cache),如下图:

image.png

  • 1.memory cache 内存中的缓存,主要包含页面加载时已经抓取的资源,比如js文件、css文件、图片、字体文件等,内存中的缓存读取速度是比硬盘中的缓存读取速度快的多的,不是一个数量级的;虽然内存缓存读取速度快,但是存储的时间却不长,会随着浏览器进程的释放而释放,比如关闭tab标签页时,内存中的缓存会释放掉。

那么什么时候会把资源缓存在内存中呢?

几乎所有的网络请求都会根据相关的策略被浏览器自动放入memory cache中,但如果需要缓存的资源过多时,因为浏览器所占用的内存是有限的,所以memory cache注定只能是‘短期存储’,当数据量过大时,即使网页不关闭,缓存依然会失效。

  • 2.disk cache

存储在硬盘中的缓存,读取速度慢,但是什么都能存储到磁盘当中,与内存相比胜在容量和存储的时效性上。MemoryCache要比DiskCache快的多。举个例子,从远程web服务器直接提取访问文件可能需要500毫秒,那么磁盘访问可能需要10-20毫秒,而内存访问只需要100纳秒。

在所有的浏览器缓存中,Disk Cache覆盖面是最大的,它会根据HTTP Hearder中的字段判断哪些资源需要缓存,哪些资源可以不请求直接使用,哪些资源过期了需要重新请求。

二、浏览器缓存的类型

Web 前端缓存大致可以分为:数据库缓存、服务器端缓存(代理服务器缓存、CDN 缓存)、浏览器缓存。

浏览器缓存也包含很多内容:HTTP 缓存、indexDB、cookie、localstorage 等等。这里我们只讨论 HTTP 缓存相关内容。

当浏览器中存在缓存数据后,可以根据是否需要向服务器发送请求,将缓存类型分为:强制缓存协商缓存

1. 强制缓存

用户请求数据,如果命中了缓存且缓存没有失效,则不向服务端请求数据,而直接从本地资源获取。

用户请求数据,如果命中了缓存且缓存失效,会向服务器重新请求资源,数据喝资源返回后,再次根据缓存规则存入浏览器缓存。

那么浏览器是怎么判断缓存是否失效呢?

强制缓存的response header中有两个字段表明失效规则(Expires/ Cache-Control);

  • 1.1 ExpiresExpires的值为服务端返回的到期时间,即下一次请求时,请求时间小于服务端返回的到期时间,直接使用缓存数据。不过ExpiresHTTP 1.0的东西,现在默认浏览器均默认使用HTTP 1.1,所以它的作用基本忽略。另一个问题是,到期时间是由服务端生成的,但是客户端时间可能跟服务端时间有误差,这就会导致缓存命中的误差。 所以HTTP 1.1 的版本,使用Cache-Control替代。

  • 1.2 Cache-ControlCache-Control 是最重要的规则。常见的取值有private、publicno-cachemax-ageno-store;

1. public: 表明响应可以被任何对象(包括:发送请求的客户端,代理服务器,等等)缓存,即使是通常不可缓存的内容;
2. private: 表明响应只能被单个用户缓存,不能作为共享缓存(即代理服务器不能缓存它)。私有缓存可以缓存响应内容,比如:对应用户的本地浏览器;
3. no-cache: 在发布缓存副本之前,强制要求缓存把请求提交给原始服务器进行验证(协商缓存验证);
4. no-store: 缓存不应存储有关客户端请求或服务器响应的任何内容,即不使用任何缓存;
  • 1.3 ExpiresCache-Control 的关系:
// 相同点
1. 都是强制缓存;

// 不同点
1. Expires是http1.0规定的,而Cache-Control是http1.1规定的;
2. Expires的过期时间采用的是绝对时间,容易造成差错; 而Cache-Control的过期时间采用的时相对时间,在缓存上不会出现问题;
3. 两者可以同时存在于一次请求中,但是不会同时在一次请求中起作用。 在HTTP1.0的环境下,Cache-Control不起作用,Expires起作用; 在HTTP1.1的环境之下, Expires不起作用,而Cache-Control起作用。当前一般都是http1.1的情况,所以Expires是作为一种向下兼容的形式而存在的;
4. Cache-Control的选择更多,功能更为强大,推荐使用。 Expires作为强缓存,功能单一,不推荐使用;

2. 协商缓存

用户请求数据,浏览器直接向服务器发送请求,协商对比服务器端和本地的资源,验证本地资源是否有效。

协商缓存一般是使用 if-modified-since/Last-Modifiedif-none-match/Etag 由服务器来决定浏览器缓存的资源是否可以使用。

  • Last-Modified / If-Modified-Since

Last-Modified:服务器响应请求时,告诉浏览器资源最后的修改时间。

If-Modified-Since:浏览器再次请求资源时,浏览器通知服务器,上次请求时,返回的资源最后修改时间。

若最后修改时间小于等于If-Modified-Since,则response header返回304,告知浏览器继续使用所保存的cache。若大于If-Modified-Since,则说明资源被改动过,返回状态码200

  • If-none-match / Etag

Etag:服务器响应请求时,告诉浏览器当前资源在浏览器的唯一标识(生成规则由服务器确定)

If-None-Match:再次请求服务器时,通过此字段通知服务器客户端缓存数据的唯一标识。服务器收到请求后发现有If-None-Match 则与被请求资源的唯一标识进行比对,不同,说明资源又被改动过,则响应整片资源内容,返回状态码200;相同,说明资源无新修改,则响应HTTP 304,告知浏览器继续使用所保存的cache

Etag 与 Last-Modified 对比:

1. 在精确度上,Etag优于Last-ModifiedLast-Modified精确到s,如果1s内,资源多次改变,Etag是可以判断出来并返回最新的资源;
2. 在性能上,Last-Modified优于Etag,因为Last-Modified只需要记录时间,而Etag需要服务器重新生成hash值,所以性能上略差;
3. 在优先级上,Etag优于Last-ModifiedEtagLast-Modified可同时存在。本地缓存时间到期后,浏览器向服务端发送请求报文,其中Request Header中包含If-none-match和Last-Modified-Since(与服务端EtagLast-Modified对比,Etag优先级高),用以验证本地缓存数据验证是否与服务端保持一致。在服务器端会优先判断Etag。如果相同,返回304;如果不同,就继续比较Last-Modified,然后决定是否返回新的资源。若服务端验证本地缓存与服务端一致,返回304,浏览器加载本地缓存;否则,服务器返回请求的资源,同时给出新的Etag以及Last-Modified时间;

3. 强制缓存和协商缓存的区别

强制缓存和协商缓存命中缓存资源后,都是从本地读取资源。如果强制缓存生效,则不需要再向服务器发出请求;而协商缓存,不管是否使用缓存,必须向服务器发送一个请求来协商。

两类缓存规则可以同时存在,强制缓存优先级高于协商缓存,也就是说,当执行强制缓存的规则时,如果缓存生效,直接使用缓存,不再执行协商缓存规则。如果强制缓存规则不生效,则需要进行协商缓存判断。

三、缓存请求流程

浏览器缓存流程:

image.png

四、实例分析

分析下掘金的部分文件的缓存策略:

image.png

注释:

  • html: 缓存有效时间为0s,页面加载时,强制浏览器每次向源服务器进行协商缓存;
  • css: 改动频率较低,允许使用本地缓存,且存在强制缓存时间(各个css文件不同,按需设置);强制缓存失效再进行协商缓存;
  • js: 允许使用本地缓存,且存在强制缓存时间(各个js文件不同,按需设置);强制缓存失效再进行协商缓存;
  • image: 不经常改变的图片,允许使用本地缓存,且存在强制缓存时间,强制缓存失效再进行协商缓存;

总结

一般在页面中,css、js、image等不同类型文件的缓存策略大致相同。即同时存在强缓存和协商缓存策略。对于强缓存,给定本地缓存的有效时间max-age,一般根据不同文件类型的确定max-age大小;对于协商缓存,给定Last-ModifiedEtag标识,服务器端验证客户端缓存的有效性。本章中给出了,官网各部件浏览器端缓存策略的简介。但是,部分文件会存在特殊的缓存设置。比如,页面中很多的jscssimage等会添加版本号,强制刷新缓存等。