之前一直对浏览器缓存只能描述一个大概,深层次的原理不能描述上来;为了泄恨,查阅一些资料最终对其有了一个更深入的理解
本文主要讲解浏览器端的缓存,缓存的作用是不言而喻的,能够极大的改善网页性能,提高用户体验。
浏览器缓存的基本认识
分为强缓存 和 协商缓存
(1)浏览器在加载资源的时候,会根据HTTP的头部header判断它是否命中强缓存,强缓存如果直接命中,那么浏览器就会直接从自己的缓存中读取资源,而不会请求到服务器。比如某个css文件,如果浏览器在加载这个文件的时候,从这个文件的http头部判断命中了缓存,那么就会直接从内存中读取这个css文件,而不会再去请求服务器。
(2)当强缓存没有命中的时候,那么浏览器就会发送http请求到服务器,通过服务器依据资源的另外一些http头部去验证这个资源是否命中协商缓存,如果协商缓存命中,服务器就将这个请求返回,但是并不会返回这个资源的数据,而是告诉浏览器可以直接从缓存中读取这个资源,那么浏览器就会从缓存中读取这个资源。
(3)强缓存和协商缓存的共同点:如果命中,那么都会从客户端的缓存中去读取资源,而不是从服务器中读取资源。不同的是,强缓存不会发送请求到服务器,而协商缓存则需要发送请求到服务器。
(4)如果协商缓存也没有命中的时候,浏览器直接从服务器中加载资源数据。
强缓存
当浏览器对某个资源的请求命中了强缓存时,返回的 http
状态为200,在 chrome
的开发者工具的 network
里面 size
会显示为 from cache
。
强缓存是利用 Expires
或者 Cache-Control
这两个 http response header
实现的,它们都用来表示资源在客户端缓存的有效期。
一,Expires
Expires
是 http1.0
提出的一个表示资源过期时间的 header
,它描述的是一个绝对时间,由服务器返回,用GMT格式的字符串表示,如:Expires:Thu, 31 Dec 2037 23:55:55 GMT,它的缓存原理是:
(1)浏览器第一次跟服务器请求一个资源的时候,服务器在返回这个资源的同时,会在 response
的 header 加上 Expires
的 header
,如
(2)浏览器在接收到这个资源后,会把这个资源连同所有的 response header
一起缓存下来(所有缓存命中的请求返回的 header
并不是来自服务器,而是来自之前缓存的 header
)
(3)浏览器再请求这个资源的时候,会先从缓存中去寻找,最好找到这个资源后,会拿当前的时间和Expire设置的时间比较,如果当前请求的时间在 Expires
指定的时间之前,那么就能命中缓存,否则不命中。
(4)如果缓存没有命中,那么浏览器直接从服务器中加载资源时,Expire Header
在重新加载的时候就会被更新。
弊端:Expires是较老的强缓存管理header,由于它是服务器返回的一个绝对时间,在服务器时间与客户端时间相差较大时,缓存管理容易出现问题,比如随意修改下客户端时间,就能影响缓存命中的结果。
所以在 HTTP1.1
的时候,就提出了一个新的header:Cache-Control
,这是一个相对的时间,在配置缓存的时候以秒为单位,用数值表示,如:Cache-Control:max-age=315360000
二,Cache-Control
(1)浏览器第一次跟服务器请求一个资源,服务器在返回这个资源的同时,在 respone
的 header
加上 Cache-Control
的 header
,如:
(2)浏览器在接收到这个资源后,会把这个资源连同所有 response header
一起缓存下来;
(3)浏览器再请求这个资源时,先从缓存中寻找,找到这个资源后,根据它第一次的请求时间和 Cache-Control
设定的有效期,计算出一个资源过期时间,再拿这个过期时间跟当前的请求时间比较,如果请求时间在过期时间之前,就能命中缓存,否则就不行。(4)如果缓存没有命中,浏览器直接从服务器加载资源时,Cache-Control Header
在重新加载的时候会被更新。
Cache-Control
描述的是一个相对时间,在进行缓存命中的时候,都是利用客户端时间进行判断,所以相比较Expires,Cache-Control的缓存管理更有效,安全一些。这两个header可以只启用一个,也可以同时启用,当response header
中,Expires
和 Cache-Control
同时存在时,Cache-Control
优先级高于Expires
:
协商缓存
当浏览器对某个资源的请求没有命中强缓存,就会发一个请求到服务器,验证协商缓存是否命中,如果协商缓存命中,请求响应返回的http状态为304并且会显示一个Not Modified
的字符串,比如你打开京东的首页,按f12打开开发者工具,再按f5刷新页面,查看 network
,可以看到有不少请求就是命中了协商缓存的:
协商缓存是利用的是【Last-Modified,If-Modified-Since】和【ETag、If-None-Match】这两对Header来管理的
一,【Last-Modified,If-Modified-Since】
(1)浏览器第一次跟服务器请求一个资源,服务器在返回这个资源的同时,在respone的header加上 Last-Modified
的header,这个header表示这个资源在服务器上的最后修改时间:
(2)浏览器再次跟服务器请求这个资源时,在request的header上加上 If-Modified-Since
的header,这个header的值就是上一次请求时返回的Last-Modified
的值:
(3)服务器再次收到资源请求时,根据浏览器传过来 If-Modified-Since
和资源在服务器上的最后修改时间判断资源是否有变化,如果没有变化则返回304 Not Modified,但是不会返回资源内容;如果有变化,就正常返回资源内容。当服务器返回304 Not Modified的响应时,response header中不会再添加 Last-Modified
的header,因为既然资源没有变化,那么 Last-Modified
也就不会改变,这是服务器返回304时的 `response header :
(4)浏览器收到304的响应后,就会从缓存中加载资源。
(5)如果协商缓存没有命中,浏览器直接从服务器加载资源时,Last-Modified
Header在重新加载的时候会被更新,下次请求时,If-Modified-Since
会启用上次返回的Last-Modified
值。
弊端:【Last-Modified,If-Modified-Since】都是根据服务器时间返回的header,一般来说,在没有调整服务器时间和篡改客户端缓存的情况下,这两个header配合起来管理协商缓存是非常可靠的,但是有时候也会服务器上资源其实有变化,但是最后修改时间却没有变化的情况,而这种问题又很不容易被定位出来,而当这种情况出现的时候,就会影响协商缓存的可靠性。
所以就有了另外一对header来管理协商缓存,这对header就是【ETag、If-None-Match】。
二,【ETag、If-None-Match】
(1)浏览器第一次跟服务器请求一个资源,服务器在返回这个资源的同时,在respone的header加上ETag
的header,这个header是服务器根据当前请求的资源生成的一个唯一标识,这个唯一标识是一个字符串,只要资源有变化这个串就不同,跟最后修改时间没有关系,所以能很好的补充Last-Modified的问题:
(2)浏览器再次跟服务器请求这个资源时,在request的header上加上If-None-Match
的header,这个header的值就是上一次请求时返回的ETag的值:
(3)服务器再次收到资源请求时,根据浏览器传过来If-None-Match
和然后再根据资源生成一个新的ETag
,如果这两个值相同就说明资源没有变化,否则就是有变化;如果没有变化则返回304 Not Modified,但是不会返回资源内容;如果有变化,就正常返回资源内容。与Last-Modified不一样的是,当服务器返回304 Not Modified的响应时,由于ETag重新生成过,response header中还会把这个ETag返回,即使这个ETag跟之前的没有变化:
(4)浏览器收到304的响应后,就会从缓存中加载资源。