浏览器缓存机制
参考资料: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-Since
和If-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-Match
和If-Modified-Since
。
3.不同浏览器下,这些用户操作的差异
我们这里就先不比较浏览器之间的区别(其实还是因为我很懒,不爱记。。。)
4.看完这三个问题之后又有一个小小的问题,from disk cache
和from memory cache
的区别?
首先,获取缓存数据,会先从内存中找,没有的话再到磁盘中找,再没有才发送请求。
from disk cache
:不请求网络资源,在磁盘当中,一般非脚本会存在内存当中,如css等,目测html一般也是。
from memory cache
:不请求网络资源,资源在内存当中,一般脚本、字体、图片会存在内存当中(当浏览器关闭,数据就清空了)。
所以浏览器打开网页,在有缓存请求的时候
4.启发式缓存阶段
如果响应头中没有失效的字段
启发式缓存阶段会从Date(创建报文的日期时间)
和Last-Modified
字段所差的时间,取10%的时间作为缓存周期。
看到这点时候我还是不明白这个时间是怎么算的,所以我决定自己试试
1) 先打开网页,记录Date
和Last-Modified
的值。
2)如何算一下过期时间
1 | const date = new Date('Sat, 26 Jan 2019 13:28:22 GMT').getTime();//Date字段的时间 |
可以看到大概19年的2月18号多
3)于是我把本地时间调整到(2019-02-17),也就是未超出启发式缓存的过期时间,打开新窗口输入url+enter。
可以看到,状态码200。没有向服务器发送请求。所以响应报文的Date字段的时间是没改变的。
4)接着,我又把本地时间设置为(2019-02-19),就是超出了启发式缓存的过期时间,打开新窗口输入url+enter。
可以看到变成了协商缓存,状态码304,并且响应头中更新了字段。
5.使用缓存的好处
1.用户体验,加载数据比较快速。
2.节省带宽
3.减轻服务器压力
最后
我们来了解一下meta标签禁止页面缓存的方式,在多次尝试之后都没成功。
网上找到的解答:如果想保证缓存控制的有效性,只能够通过网络服务器配置缓存控制了,因为meta标签的解析实现不是所有浏览器的必然支持的。你给了某个标签,也只是建议浏览器应该怎么做而已,具体的浏览器对页面缓存的设置可能会让表现有异常
也就是说,浏览器放弃meta用于缓存控制的标签了。
关于meta标签禁止缓存失效问题