一、什么是缓存
网上讲了一大堆官方的说辞,看绕了反而,我这里通俗的理解就是,当浏览器向服务器请求一次数据之后,再次请求相同数据的时候,不需要再次向服务器读取数据,直接从本地缓存中读取。
这样就减少了和服务器读取数据的次数,更加高效。
一般前端程序员更关注的是浏览器的缓存,所以这篇文章也是以浏览器缓存为基石~
二、强制缓存和协商缓存
-
强制缓存:不会向服务器发送请求,直接从缓存中读取资源;从浏览器控制台的network选项中可以看到该请求返回
200
状态码 -
协商缓存:在使用本地缓存之前,需要向服务器发送请求,服务器会根据请求头一些参数判断是否命中协商缓存,如果命中就直接返回
304
状态码,并且带上返回头通知浏览器从缓存中读取资源
大家看到这里肯定在想,有了缓存直接读取就行了,为什么还要出现协商缓存,向服务器询问呢,这不是多此一举吗?其实,如果浏览器不询问服务器而直接从缓存中读取数据,加入服务器中数据已经进行了修改怎么办?所以这个方法可以保证浏览器每次拿到的数据都是最新状态的;
强制缓存的header参数
-
Expires:response请求头中的过期时间,浏览器再次加载资源的时候,如果还在过期时间内,则命中强缓存;(以分钟为单位,但是这个字段中的时间有点问题,所以后面http协议中新出了一个字段cache-control,优先级比cache-control低)
-
Cache-Control:当值为max-age=300(单位为s)的时候,则表示5分钟内再次请求相同资源的时候,会命中强制缓存。这个字段还有一些常用的设置值:
-no-cache
:不使用本地缓存,需要使用协商缓存-no-store
:直接禁止浏览器缓存数据,每次用户请求该资源,都会向服务器发送一个请求,每次都会下载完整的资源;-public
:可以被所有用户缓存,包括终端用户和CDN等中间代理服务器-private
:智能被终端用户的浏览器缓存,不允许CDN等中继缓存服务器对其缓存;
协商缓存的header参数
Last-Modify/if-Modify-Since
:浏览器第一次请求一个资源的时候,服务器返回的header
中会加一个Last-Modify
,标识这个资源上一次修改的时间;当浏览器再次请求这个资源的时候,请求头中会包含if-Modify-Since
标识上一次修改时间,即Last-Modify
,服务器根据这个时间和自己手里资源修改时间进行对比,如果相等,则命中协商缓存,返回状态码304
,如果状态码为200
,表示需要向服务器重新获取数据;
需要注意的是,Last-Modify判断协商缓存有点问题,因为这个字段以秒为单位,当资源在毫秒内修改了,可能读取不出来,Etag可以解决这个问题.
Etag/if-None-Match
:etag
是一个hash
值,web服务器响应请求的时候,告诉浏览器当前资源在服务器上的唯一标识。下次浏览器再次向服务器请求资源的时候就会带着这个etag(保存在if-None-Match
字段中)去询问服务器,服务器拿到这个etag之后会和服务器内部的etag进行比对,看是否相等,如果相等,说明资源没被修改,返回状态码304,如果修改了,etag值会发生变化,返回状态码200。
需要注意的是,etag的值是hash值,需要服务器生成,这样会加大服务器的开销
三、浏览器缓存过程总结
知道上面的一些概念之后,再来看浏览器缓存的过程,我们就会更加清晰:
-
浏览器第一次加载资源,服务器返回200,浏览器将资源从服务器上下载下来,并把返回头即该请求的返回时间一并缓存;
-
下一次加载资源的时候,先比较当前时间和上次返回200的时间差,看是否超过
cache-control
的max-age
,如果没有过期,直接命中强缓存,不发送请求直接从本地缓存中读取该文件,如果是http1.1
以下版本,就看Expires
字段进行判断是否过期; -
服务器收到请求之后,优先根据
Etag
值判断该请求的资源是否被修改,Etag
值一致则命中协商缓存,返回304
,如果不一致,返回新的资源带上新的Etag
并返回200
; -
如果服务器受到的请求没有
etag
值,就将if-modified-Since
和被请求的文件的最后修改时间进行对比,一致则命中协商缓存,返回304
,不一致就返回新的last-modified
和文件并返回状态码200
用一张完整的流程图,如下所示: