缓存的重要性不言而喻,通过网络请求资源缓慢并且降低了客户端的用户体验,增加了服务器的负担。很多短期内不会经常发生变化的资源文件没必要每次访问都向服务器进行数据请求,而缓存策略的使用就是为了改善客户端的呈现时间,降低服务器的负担。
对于HTTP缓存机制来说,策略体现在HTTP头部信息的字段上,这些策略根据是否需要重新向服务器端发起请求可以分为强缓存和协商缓存两大类。
强缓存:
强缓存紧密连接着一个缓存时间期限,浏览器请求资源时会查看缓存中的资源是否存在并确定该缓存资源是否已过期,若没有过期,则取得缓存中的资源。
协商缓存:
可见协商缓存无论如何都要与服务器进行交互,比强缓存稍微复杂些。但是两者是相辅相成并且可以共存的,强缓存优先级较高,意味着请求一个资源时会先比较强缓存中的字段,如果命中则不会再执行接下来的协商缓存的过程。
1、强缓存
与强缓存相关的HTTP 响应头的字段有两个Expires以及Cache-Control
Expires是HTTP1.0的规范,且客户端与服务器的时间会有误差,因此使用HTTP1.1的Cache-Control来替代Expires.
(1)Expires
该字段规定了缓存的资源的过期时间,在此之前,缓存中的资源都是有效的。如Tue, 27 Feb 2018 06:37:48 GMT,它的时间期限是服务器生成的,与客户端的时间会存在误差,固定时间,是HTTP1.0时的规范。优先级比cache-control低。
(2)Cache-Control
public:代表请求返回的内容在所有经过的路径中(包括代理以及客户端浏览器)都可以对返回的内容进行缓存。
private:代表只有发起请求的浏览器才可以进行缓存;
no-cache:可以在本地进行缓存,但是每次发请求时,都要向服务器进行验证,如果服务器允许,擦能使用本地缓存;
no-store:本地和代理服务器都不可以使用缓存,每次都要重新请求。
max-age=:缓存多少秒后过期。
其中最常用的max-age单位为秒,对比expires体现着一个相对时间,即多少秒后缓存资源失效。
例如:
图中Cache-Control仅指定了max-age,所以默认为private,缓存时间为31536000秒(365天)
也就是说,在365天内再次请求这条数据,都会直接获取缓存数据库中的数据,直接使用。
需要注意的一点是:no-cache不是说不准使用缓存,而是需要走接下来优先级相对较低的另一类--协商缓存。真正决定不用缓存内的资源的是字段no-store。
2、协商缓存
协商缓存是通过客户端与服务器进行HTTP通信时,互送缓存标识。
缓存标识可以分为两种:
- Last-Modefied和if-Modefined-Since
第一次请求某个资源时,由于一定不会走缓存,所以服务器端会在响应头中加上一个形如:Last-Modified:Mon, 26 Feb 2018 06:37:41 GMT
的字段告诉客户端,这个资源上次最后修改的时间。当刷新页面再次请求时,这时候的协商缓存会在请求头中加上一个形如:If-Modified-Since:Mon, 26 Feb 2018 06:37:41 GMT
的字段。判断上一次时间后是否修改了,让服务器去判断是否在此时间后资源内容发生了变化。
若资源的Last-Modified大于If-Modified-Since,说明资源又被改动过,则响应整片资源内容,返回状态码200;
若资源的Last-Modified小于或等于If-Modified-Since,说明资源无新修改,则响应HTTP 304,告知浏览器继续使用所保存的cache。
如果服务器端发现资源改变了,就返回200以及新的资源给客户端,若没有修改,返回304 Not Modified 让客户端从缓存中取。
- Etag和if-None-Match
同样第一次客户端请求资源时,服务端随资源在响应头部返回一个字段Etag,形如: ETag:W/"1823823287"。
该字段的值是该资源在服务器端的唯一标识,生成Etag的策略由服务器决定。当资源发生变化时该值也发生变化。下一次客户端请求同一资源时,在请求头将这次得到的值放在字段if-None-Match中发送给服务端。
如果服务器端发现资源改变了,就返回200以及新的资源给客户端,若没有修改,返回304 Not Modified 让客户端从缓存中取。
假如使用强缓存,如果服务器内的资源更新了,客户端在没有强制刷新的情况下,看到的内容还是旧的??
缓存的意义就在于减少请求,更多的使用本地的缓存,给用户更好体验的同时,也减轻服务器压力。所以最佳实践就是尽可能命中强缓存,同时,能在更新版本的时候让客户端的缓存失效。
在更新版本之后,如何让用户在第一时间使用最新的资源文件呢?在更新版本的时候,顺便把静态资源的路径改了,这样就相当于第一次访问这些资源,就不会存在缓存的问题了。使用webpack在打包的时候,在文件的命名上带上hash值。
所以一个较为合理的缓存方案:
HTML:使用协商缓存;
CSS、JS、图片:使用强缓存,文件名带上hash值。