目录
前言
对于浏览器缓存,每个前端开发者应该都不会陌生,同时它也是我们在日常开发中存在的一个非常重要的优化手段,无论在节省带宽、提高加载和渲染速度、减少网络阻塞,以及提高用户体验上,都发挥着很重要的作用。
前端缓存主要是分为HTTP缓存和浏览器缓存。其中HTTP缓存是在HTTP请求传输时用到的缓存,主要在服务器代码上设置;而浏览器缓存则主要由前端开发在前端js上进行设置。
目的
缓存可以说是性能优化中简单高效的一种优化方式了。一个优秀的缓存策略可以缩短网页请求资源的距离,减少延迟,并且由于缓存文件可以重复利用,还可以减少带宽,降低网络负荷;
对于一个数据请求来说,可以分为发起网络请求、后端处理、浏览器响应三个步骤。浏览器缓存可以帮助我们在第一和第三步骤中优化性能。比如说直接使用缓存而不发起请求,
或者发起了请求但后端存储的数据和前端一致,那么就没有必要再将数据回传回来,这样就减少了响应数据。
缓存过程分析
浏览器与服务器通信的方式为应答模式,即是:浏览器发起HTTP请求 – 服务器响应该请求。那么浏览器第一次向服务器发起该请求后拿到请求结果,
会根据响应报文中HTTP头的缓存标识,决定是否缓存结果,是则将请求结果和缓存标识存入浏览器缓存中,简单的过程如下图:
可以看出:浏览器每次发起请求,都会先在浏览器缓存中查找该请求的结果以及缓存标识;
浏览器每次拿到返回的请求结果都会将该结果和缓存标识存入浏览器缓存中。
强制缓存(强缓存)
强制缓存就是向浏览器缓存查找请求结果,并根据请求结果的缓存规则来决定是否使用该缓存结果的过程。
分以下三种情况:
- 不存在该缓存结果和缓存标识,强制缓存失效,则直接向服务器发起请求(跟第一次发起请求一致)
- 存在该缓存结果和缓存标识,但该结果已失效,强制缓存失效,则使用协商缓存。
-
存在该缓存结果和缓存标识,且该结果尚未失效,强制缓存生效,直接返回该结果
强制缓存的缓存规则
当浏览器向服务器发起请求时,服务器会将缓存规则放入HTTP响应报文的HTTP头中和请求结果一起返回给浏览器,控制强制缓存的字段分别是Expires和Cache-Control,
其中Cache-Control优先级比Expires高。
Expires
Expires是HTTP/1.0控制网页缓存的字段,其值为服务器返回该请求结果缓存的到期时间,即再次发起该请求时,如果客户端的时间小于Expires的值时,直接使用缓存结果。
到了HTTP/1.1,Expire已经被Cache-Control替代,原因在于Expires控制缓存的原理是使用客户端的时间与服务端返回的时间做对比,那么如果客户端与服务端的时间因为某些原因(例如时区不同;
客户端和服务端有一方的时间不准确)发生误差,那么强制缓存则会直接失效,这样的话强制缓存的存在则毫无意义。
Cache-Control
在HTTP/1.1中,Cache-Control是最重要的规则,主要用于控制网页缓存,主要取值如下:
public:所有内容都将被缓存(客户端和代理服务器都可缓存)
private:所有内容只有客户端可以缓存,Cache-Control的默认取值
no-cache:客户端缓存内容,但是是否使用缓存则需要经过协商缓存来验证决定
no-store:所有内容都不会被缓存,即不使用强制缓存,也不使用协商缓存
max-age=xxx:缓存内容将在xxx秒后失效
栗子:
可以看出:
HTTP响应报文中expires的时间值,是一个绝对值
HTTP响应报文中Cache-Control为max-age=600,是相对值
由于Cache-Control的优先级比expires高,那么直接根据Cache-Control的值进行缓存,就是说在600秒内再次发起该请求,则会直接使用缓存结果,强制缓存生效。
注:在无法确定客户端的时间是否与服务端的时间同步的情况下,Cache-Control相比于expires是更好的选择,所以同时存在时,只有Cache-Control生效。
浏览器的缓存存放在哪里
Size值则代表该缓存存放的位置,分别为memory cache 和 disk cache。那么memory cache 和disk cache又分别代表的是什么呢?
memory cache代表使用内存中的缓存,disk cache则代表使用的是硬盘中的缓存,浏览器读取缓存的顺序为memory –> disk –> 服务器请求
首次访问
关闭标签,重新打开
刷新页面
内存缓存(from memory cache):内存缓存具有两个特点,分别是速度快和时间限制。
硬盘缓存(from disk cache):硬盘缓存则是直接将缓存写入硬盘文件中,读取缓存需要对该缓存存放的硬盘文件进行I/O操作,然后重新解析该缓存内容,读取复杂,速度比内存缓存慢。
在浏览器中,浏览器会在js和图片等文件解析执行后直接存入内存缓存中,那么当刷新页面时只需直接从内存缓存中读取(from memory cache);
而css文件则会存入硬盘文件中,所以每次渲染页面都需要从硬盘读取缓存(from disk cache)。
因为CSS文件加载一次就可渲染出来,我们不会频繁读取它,所以它不适合缓存到内存中,但是js之类的脚本却随时可能会执行,如果脚本在磁盘当中,我们在执行脚本的时候需要从磁盘取到内存中来,
这样IO开销就很大了,有可能导致浏览器失去响应。
三级缓存原理
- 先去内存看,如果有,直接加载
- 如果内存没有,择取硬盘获取,如果有直接加载
- 如果硬盘也没有,那么就进行网络请求
- 加载到的资源缓存到硬盘和内存,下次请求可以快速从内存中获取到
协商缓存
协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程
主要有以下两种情况:
-
协商缓存生效,返回304
- 协商缓存失效,返回200和请求结果结果
同样,协商缓存的标识也是在响应报文的HTTP请求头中和请求结果一起返回给浏览器的,控制协商缓存的字段分别有:Last-Modified / If-Modified-Since和Etag / If-None-Match,
其中Etag / If-None-Match的优先级比Last-Modified / If-Modified-Since高
Last-Modified / If-Modified-Since
Last-Modified是服务器响应请求时,返回该资源文件在服务器最后被修改的时间
If-Modified-Since则是客户端再次发起该请求时,携带上次请求返回的Last-Modified值,通过此值告诉服务器该资源上次请求返回的最后被修改时间
服务器收到该请求,发现请求头含有If-Modified-Since字段,则会根据If-Modified-Since的字段值与该资源在服务器的最后被修改时间做对比,若服务器的资源最后被修改时间大于If-Modified-Since的字段值,则重新返回资源,状态码为200;否则则返回304,代表资源无更新,可继续使用缓存文件。
Etag / If-None-Match
Etag是服务器响应请求时,返回当前资源文件的一个唯一标识(由服务器生成)
If-None-Match是客户端再次发起该请求时,携带上次请求返回的唯一标识Etag值,通过此字段值告诉服务器该资源上次请求返回的唯一标识值
根据If-None-Match的字段值与该资源在服务器的Etag值做对比,一致则返回304,代表资源无更新,继续使用缓存文件;不一致则重新返回资源文件,状态码为200
注:Etag / If-None-Match优先级高于Last-Modified / If-Modified-Since,同时存在则只有Etag / If-None-Match生效。对于协商缓存,使用强制刷新可以使得缓存无效。
但是对于强缓存,在未过期时,必须更新资源路径才能发起新的请求。
更新缓存
- 在资源的内容更改后,更改资源的网址,强制用户下载新响应。比如在资源链接后添加参数、随机数
- no-cache浏览器会缓存,但刷新页面或者重新打开时会请求服务器,服务器可以响应304,如果文件有改动就会响应200
- no-store浏览器不缓存,刷新页面需要重新下载页面
合理应用缓存
强制缓存优先级最高,并且资源的改动在缓存有效期内都不会对缓存产生影响,因此该方法适用于大型且不易修改的的资源文件,例如第三方CSS、JS文件或图片资源。
建议将此类大型资源存入disk cache
,因为存在硬盘中的文件资源不易丢失。
协商缓存灵活性高,适用于数据的缓存,根据上述方法的对比,采用Etag
标识进行对比灵活度最高,并考虑将数据存入内存中,因为内存加载速最快,并且数据体积小,不会占用大量内存资源。