第一阶段:无缓存
浏览器向服务器请求资源 a.jpg,服务器找到对应资源把内容返回给浏览器。当浏览器再次向服务器请求资源a.jpg时,服务器重新发送完整的数据文件给浏览器。
- 优点:简单,啥都不用做
- 缺点:每次请求都查找并返回原始文件,浪费带宽(1990年的带宽很贵啊)
第二阶段:有缓存无更新
浏览器第一次请求a.jpg 时服务器会发送完整的文件,浏览器可以把这个文件存到本地(缓存),下次再需要这个文件时直接从本地获取就行了,这样就能省下带宽了。
- 优点: 省带宽
- 缺点: 如果服务器上a.jpg的文件内容变了,浏览器每次都从缓存读取无法获取最新文件
第三阶段: 缓存+更新机制
浏览器第一次请求a.jpg 时服务器会发送完整的文件,服务器在发送文件的时候还附带发送一些额外信息——过期时间,如 Expires: Mon,10 Dec 1990 02:25:22GMT。浏览器可以把这个文件和额外信息存到本地。当再次需要a.jpg的时候浏览器用当前浏览器时间和Expires做个比较,如果当前时间在过期时间以内,就直接使用缓存文件((200, from xx cache);如果在过期时间以外就重新向服务器发送请求要资源(200)。 服务器在每次给资源的时候都会发送新的过期时间
- 优点:缓存可控制
- 缺点:控制的功能太单一;这种格式的时间很容易写错
第四阶段: 缓存+更新机制升级版
比如:浏览器第一次请求a.jpg 时,服务器会发送完整的文件并附带额外信息Cache-Control: max-age=300
;
浏览器把文件和附带信息保存起来。当再次需要a.jpg 时,如果是在300秒以内发起的请求则直接使用缓存(200, from xx cache),否则重新发起网络请求(200)。下面是Cache-Control常见的几个值:
1.Public表示响应可被任何中间节点缓存,如 Browser <-- proxy1 <-- proxy2 <-- Server,中间的proxy可以缓存资源,比如下次再请求同一资源proxy1直接把自己缓存的东西给 Browser 而不再向proxy2要。
2.Private表示中间节点不允许缓存,对于Browser <-- proxy1 <-- proxy2 <-> Server,proxy 会老老实实把Server 返回的数据发送给proxy1,自己不缓存任何数据。当下次Browser再次请求时proxy会做好请求转发而不是自作主张给自己缓存的数据。
3.no-cache表示不使用 Cache-Control的缓存控制方式做前置验证,而是使用 Etag 或者Last-Modified字段来控制缓存
no-store ,真正的不缓存任何东西。浏览器会直接向服务器请求原始文件,并且请求中不附带 Etag 参数(服务器认为是新请求)。
4.max-age,表示当前资源的有效时间,单位为秒。
- 优点:缓存控制功能更强大
- 缺点:假如浏览器再次请求资源a.jpg的时间间隔超过了max-age,这时候向服务器发送请求服务器应该会重新返回a.jpg的完整文件。但如果 a.jpg 在服务器上未做任何修改,发送a.jpg的完整文件就太浪费带宽了,其实只要发送一个「a.jpg未被更改」的短消息标示就好了。
第五阶段: 缓存+更新机制终极版
比如:浏览器第一次请求a.jpg 时,服务器会发送完整的文件并附带额外信息,其中Etag 是 对a.jpg文件的编码,如果a.jpg在服务端未被修改,这个值就不会变Cache-Control: max-age=300;ETag:W/"e-cbxLFQW5zapn79tQwb/g6Q"
浏览器把a.jpg和额外信息保存到本地。假如浏览器在300秒以内再次需要获取a.jpg时,浏览器直接从缓存读取a.jpg(200, from xx cache)。假如浏览器在300秒之后再次需要获取a.jpg时,浏览器发现该缓存的文件已经不新鲜了,于是就向服务器发送请求 重新获取a.jpg, 在发送请求的时候附带刚刚保存的a.jpg的ETag ( If-None-Match:W/"e-cbxLFQW5zapn79tQwb/g6Q")。 服务器在接收到请求后拿浏览器请求的 Etag 和当前文件重新计算后端 Etag 做个比较,如果二者相等表示文件在未修改则发送个短消息(响应头,不包含图片内容, 304),如果二者不等则发送新文件和新的 ETag,浏览器获取新文件并更新该文件的 Etag。
与 ETag 类似功能的是Last-Modified/If-Modified-Since
。当资源过期时(max-age超时),发现资源具有Last-Modified声明,则再次向web服务器请求时带上头 If-Modified-Since,表示上次服务器告知的文件修改的时间。web服务器收到请求后发现有头If-Modified-Since 则与被请求资源的最后修改时间进行比对。若最后修改时间较新,说明资源又被改动过,则响应整片资源内容(200);若最后修改时间较旧,说明资源无新修改,则响应HTTP 304 ,告知浏览器继续使用所保存的cache。
总结:
浏览器在请求已经访问过的URL的时候, 会判断是否使用缓存, 判断是否使用缓存主要通过判断缓存是否在有效期内, 通过两个字段来判断:
- Expires, 有效期, 返回的是一个GMT时间, 但是使用的是客户端时间, 与服务器时间存在一定时间差
- Cache-Control => max-age, 最大有效时间, 单位是s, 优先级比expires高, 为了解决expires时间差的问题而出现的
当缓存过期后, 浏览器不会直接去服务器上拿缓存, 而是判断缓存是否有更新, 能否继续使用, 判断的方法有两种:
- Last-Modified 和 If-Modified-Since: 服务器会响应一个Last-Modified字段, 表示最近一次修改缓存的时间, 当缓存过期后, 浏览器就会把这个时间放在If-Modified-Since去请求服务器, 判断缓存是否有更新
- Etag和If-None-Match: 服务器会响应一个Etag字段, 一个表示文件唯一的字符串, 一旦文件更新, Etag也会跟着更改, 当缓存过期后, 浏览器会把这个字符串放在If-None-Match去请求服务器, 判断是否有更新, Etag的优先级比Last-Modified的更高, Etag的出现, 是为了解决一个缓存文件在短时间内被多次修改的问题, 因为Last-Modified只能精确到秒