浏览器缓存机制

浏览器缓存机制

参考资料:https://www.cnblogs.com/chenqf/p/6386163.html

https://juejin.im/post/5a6c87c46fb9a01ca560b4d7

http://www.cnblogs.com/skynet/archive/2012/11/28/2792503.html

https://my.oschina.net/leejun2005/blog/369148

感觉写的真的很清楚,图片资源也是来自这里

假设浏览器存储缓存信息的的地方是缓存数据库

🌰都是在chrome下的调试。

1.缓存规则

第一次请求数据,缓存数据库中不存在,于是请求服务器,将服务器返回数据和缓存规则,再存入缓存数据库中

2.分类

强制缓存

1)缓存命中(客户端发起请求,缓存数据库中存在并且缓存数据未失效,返回数据)

2)缓存未命中(客户端发起请求,缓存数据库中失效,向服务器发请求,将服务器返回的数据和缓存规则再存入缓存数据库中)

那怎么知道缓存的数据是否失效

上面一直提到的缓存规则是包含在响应header中。

对于强制缓存来说,响应header中会有两个字段来标明失效规则(Expires/Cache-Control)。

Expires:这个是服务器返回的到期时间,请求时间如果小于这个时间,就直接取缓存数据。(这个是http1.0的字段,现在浏览器基本使用http1.1)。

Cache-Control:http1.1版本使用的,作为前端我觉得需要知道的属性:

1.private:客户端可以缓存(默认)。
2.max-age = xxx :缓存内容将在xxx秒后失效(大于0时直接从缓存里取数据,小于等于0时发送请求验证数据是否被修改)。
3.no-cache:需要使用协商缓存来验证缓存数据(就是强制每次请求直接发送给服务器,而不取本地缓存,并不是不缓存的意思)。
4.no-store:所有内容都不缓存。

协商缓存(对比缓存)

1.缓存命中(客户端获取缓存数据标识,请求服务器验证数据是否失效,未失效的话只返回header部分,不再需要将报文主体部分返回给客户端,通过状态码通知客户端使用缓存,客户端收到后再存入缓存数据)

举个🌰

第一次打开页面,状态码200,Size是1.4kb,Time是56ms

刷新一下

看到状态码变为304,Size是158b,Time是62ms

2)缓存未命中(客户端获取缓存数据标识,请求服务器验证数据是否失效,失效的话,返回新的数据和缓存规则,客户端收到后再存入缓存数据库)

那么是怎么判断协商缓存是否失效

有两种标识:

第一种:Last-Modified / If-Modified-Since

Last-Modified:服务器响应的时候,在响应头,返回资源最后的修改时间

举个🌰

If-Modified-Since:

刷新一下,If-Modified-Since在请求头中,带着上次服务器返回的资源最后修改时间,服务器发现有这个字段,就会与资源的最后修改时间比较,有改动的话状态码返回200,无改动的话状态码返回304并且使用缓存的数据。

举个🌰,修改一下服务器上的资源,在刷新一下页面试试:

可以看到状态码为200,Last-Modified时间被更新,If-Modified-Since的时间是上一次请求资源的最后修改时间。

第二种:Etag / If-None-Match(优先级高于Last-Modified / If-Modified-Since)

Etag:服务器响应时,告诉浏览器当前资源在服务器的唯一标识。

If-None-Match:

刷新一下,请求头中带着这个字段,服务器接收到之后,会与被请求资源比对,不同的话返回状态码200,相同的话返回304并且使用缓存数据。

为什么有了Last-Modified还要有Etag

原因之一:Last-Modified只精确到秒,一秒内可能多次文件改变。(我觉得这个比较重要)

3.这里有三个问题我不明白

1. url+enter、刷新、强制刷新打开网页的区别。
1)url+enter打开: 状态码为200,并且可以看到from disk cache,从磁盘中取出缓存,没有发请求给服务器,而是直接取缓存数据。

2)强制刷新:不管浏览器是否缓存,都要重新去服务器请求资源,成功返回200。

Cache-Control:no-cache请求头中表示不使用缓存。

Cache-Control:no-cache响应头中表示客户端不缓存。

3)刷新:带上If-Modified-SinceIf-None-Match发送给服务器并且带上Cache-Control:max-age=0,验证是否可以使用缓存数据。

2.为什么刷新的时候,不直接取未失效的本地缓存,而是发送请求到服务器,验证缓存是否失效。

这与用户行为有关

用户操作 Expires/Cache-Control Last-Modified/Etag
url+enter 生效 生效
刷新 失效 生效
强制刷新 失效 失效

可以看到,刷新的时候跳过强缓存,但是会检查协商缓存,请求头会带上Cache-Control:max-age=0

强制刷新的时候,都跳过,直接向服务器请求,请求头Cache-Control:no-cache,并且请求头没有If-None-MatchIf-Modified-Since

3.不同浏览器下,这些用户操作的差异

传送门

我们这里就先不比较浏览器之间的区别(其实还是因为我很懒,不爱记。。。)

4.看完这三个问题之后又有一个小小的问题,from disk cachefrom memory cache的区别?
首先,获取缓存数据,会先从内存中找,没有的话再到磁盘中找,再没有才发送请求。
from disk cache:不请求网络资源,在磁盘当中,一般非脚本会存在内存当中,如css等,目测html一般也是。

from memory cache:不请求网络资源,资源在内存当中,一般脚本、字体、图片会存在内存当中(当浏览器关闭,数据就清空了)。

WX20181224-171812@2x

所以浏览器打开网页,在有缓存请求的时候

4.启发式缓存阶段

如果响应头中没有失效的字段

启发式缓存阶段会从Date(创建报文的日期时间)Last-Modified字段所差的时间,取10%的时间作为缓存周期

看到这点时候我还是不明白这个时间是怎么算的,所以我决定自己试试
1) 先打开网页,记录DateLast-Modified的值。

WX20190207-212830@2x

2)如何算一下过期时间
1
2
3
4
5
const date = new Date('Sat, 26 Jan 2019 13:28:22 GMT').getTime();//Date字段的时间
const modified = new Date('Wed, 13 Jun 2018 02:41:14 GMT').getTime();//Last-Modified字段的时间
const cacheTime = (date - modified)/10;//缓存的时间
const expiresTime = new Date(cacheTime + date);//Date字段的时间加上缓存的时间
console.log(expiresTime);//2019-02-18T07:21:04.800Z

可以看到大概19年的2月18号多

3)于是我把本地时间调整到(2019-02-17),也就是未超出启发式缓存的过期时间,打开新窗口输入url+enter。

WX20190207-213024@2x

可以看到,状态码200。没有向服务器发送请求。所以响应报文的Date字段的时间是没改变的。

4)接着,我又把本地时间设置为(2019-02-19),就是超出了启发式缓存的过期时间,打开新窗口输入url+enter。

WX20190219-213258@2x

可以看到变成了协商缓存,状态码304,并且响应头中更新了字段。

5.使用缓存的好处

1.用户体验,加载数据比较快速。
2.节省带宽
3.减轻服务器压力

最后

我们来了解一下meta标签禁止页面缓存的方式,在多次尝试之后都没成功。

网上找到的解答:如果想保证缓存控制的有效性,只能够通过网络服务器配置缓存控制了,因为meta标签的解析实现不是所有浏览器的必然支持的。你给了某个标签,也只是建议浏览器应该怎么做而已,具体的浏览器对页面缓存的设置可能会让表现有异常

也就是说,浏览器放弃meta用于缓存控制的标签了。
关于meta标签禁止缓存失效问题