大家好,我是辉夜真是太可爱啦 。故事的开始,要从一道经典的面试题开始说起, 从输入 URL 到页面加载完成,发生了什么? 相信大家应该对这个题目也很耳熟了。本系列是我 《一文搞懂JS系列》 之后的第二个系列。旨在让大家搞懂这个加载过程,从简答以及深入解答都能够对答如流!
前言
大家好,我是辉夜真是太可爱啦 。
在浏览器向服务器发起请求此之前,其实还有一步先查看是否有缓存。
毕竟,网站上的有些资源其实早前可能已经获取过,既然本地直接有,为何还要大费周章地请求服务器再去拿一遍呢!
所以,缓存的存在,自然有它的一定优势:
- 减少了不必要的数据传输,节省带宽
- 减少服务器的负担,提升网站性能
- 加快了客户端加载网页的速度,提升用户体验
先上思维导图:
image.png那么,辉夜 就在想,我把网站上的所有东西,全部都缓存起来,那么,下次打开岂不是更快,顿时感觉自己是 性能优化大师 。
事情哪有那么顺利 ,结果可想而知,当资源有更改的时候,如果客户端不及时更新会造成用户获取信息滞后,用户依旧使用的是缓存中的老版本,而且如果老版本有bug的话,情况那就只会更加糟糕。
所以,彻底弄懂 HTTP
缓存,在工作中更合理地使用缓存,是十分重要的。
那么首先,HTTP缓存其实分为两类,那就是 强缓存
和 协商缓存
。
强缓存
强缓存的强,是 强制
的意思。听名字也知道,强缓存
的优先级在 协商缓存
的前面。
它不需要发送请求到服务端,直接读取浏览器本地缓存,不得不说,确实挺 强 的。这下都直接把请求服务器的步骤都给去了,直接自给自足。
然后,在强缓存中,又分为两种:
image.png-
from memory cache
不访问服务器,直接读缓存,从内存中读取缓存。此时的数据时缓存到内存中的,当浏览器关闭后,数据将不存在。
-
from disk cache
不访问服务器,直接读缓存,从磁盘中读取缓存,当浏览器关闭后,数据还是存在。
强缓存主要由三个字段名来控制,Expires
Cache-Control
Pragma
,我们按照优先级从低到高依次介绍:
(存储在硬盘或者是内存,并不是由这三个字段名来控制的,浏览器在获取资源之后(无论是强缓存或者协商缓存或者网络重新获取中取得的资源),就会将资源存储到内存中,下一次再获取的时候,就可以从内存中获取到了,当浏览器关闭之后,内存中的资源清空)
Expires
Expires 指的是过期时间,它的值是 GMT时间
。在浏览器发起请求时,会根据系统时间和 Expires 的值进行比较,如果系统时间超过了 Expires 的值,那么就代表缓存失效了。
由于系统时间和服务器时间不一致的时候,就会存在校验不准确的情况,而且,系统时间是可以用户自行设定的。所以,它的优先级自然是最低的。
Cache-Control
顾名思义,缓存控制。它是一个对象,比较常用的属性值有:
-
max-age
直白翻译就是最大年龄,单位是秒。缓存时间计算的方式是距离发起的时间的秒数,超过间隔的秒数缓存失效。
这里可以将长时间内稳定不变的资源设为很大的值,例如网站的
Logo
等文件。 -
no-cache
不使用强缓存,需要与服务器验证缓存是否新鲜(即使用协商缓存)。
这种方法就是它每次都会去服务器做一次验证,验证当前是否是最新的资源,如果过期了,则从服务器再重新获取一份。可以对那些经常需要更新的资源文件采用这种方式。
-
no-store
禁止使用缓存(包括协商缓存),每次都向服务器请求最新的资源。
这种方法能保证每次获取到最新的资源,但是缺点也很明显,就是它每次都会重新去服务器拉去一下资源文件。可以对那些必须是最新资源的文件(实时性极高的文件)采用这种方式。
-
private
专用于个人的缓存,中间代理、CDN 等不能缓存此响应。
-
public
响应可以被中间代理、CDN 等缓存
-
must-revalidate
在缓存过期前可以使用,过期后必须向服务器验证
Pragma
Pragma 只有一个属性值,就是 no-cache ,效果和 Cache-Control 中的 no-cache 一致,不使用强缓存,需要与服务器验证缓存是否新鲜,在 3 个头部属性中的优先级最高。
协商缓存
当设置了不走强缓存,或者是强缓存过期失效的时候,就会走协商缓存。
协商缓存,就是客户端和服务器之间互相协商的结果。
-
Last-Modified / If-Modified-Since
二者的值都是
GMT
格式的时间字符串。-
浏览器第一次向服务器发起请求,服务器的
Respone Header
中加上Last-Modified
标记,表示此资源的最后修改时间。 -
浏览器再次跟服务器请求这个资源时,在
Request Header
上加上If-Modified-Since
标记,这个Header 的值就是上一次请求时返回的Last-Modified
的值 -
服务器接收到资源请求之后,将接收到的
If-Modified-Since
和现在资源的最后修改时间进行对比。如果无变化,则返回
304 Not Modified
,但是不会返回资源信息 。如果有变化,则返回新的资源内容,以及新的
Last-Modified
标记。 -
浏览器收到 304 的响应后,就会从缓存中加载资源
-
如果没有命中缓存,浏览器直接从服务器加载资源时,
Last-Modified
的 Header 在重新加载的时候会被更新,下次请求时,If-Modified-Since
会启用上次返回的Last-Modified
值
这种方式看似完美无瑕,但是,也有它的一定缺陷:
① 一些文件也许会周期性的更改,但是他的内容并不改变(仅仅改变的修改时间),这个时候我们并不希望客户端认为这个文件被修改了,而重新GET;
② 某些文件修改非常频繁,比如在秒以下的时间内进行修改,(比方说1s内修改了N次),If-Modified-Since能检查到的粒度是秒级的,这种修改无法判断(或者说UNIX记录MTIME只能精确到秒);
③ 某些服务器不能精确的得到文件的最后修改时间。
所以,为了解决上述中可能会出现的问题,后面就有了
ETag / If-None-Match
的出现。ETag / If-None-Match
和Last-Modified / If-Modified-Since
可以同时出现,但是ETag / If-None-Match
的优先级更高 。 -
-
ETag / If-None-Match
ETag
是服务器自动生成或者由开发者生成的对应资源在服务器端的 唯一hash标识符 ,例如 67690D623E0034098C6998E9233565A7
。
当服务端的文件变化的时候,它的 hash码会随之改变。其判断过程与 Last-Modified/If-Modified-Since
类似。
ETag
又有强弱校验之分,如果 hash 码是以 W/
开头的一串字符串,例如 W/"5eb03d6a-36a9"
,说明此时协商缓存的校验是弱校验的,只有服务器上的文件差异(根据 ETag 计算方式来决定)达到能够触发 hash 值后缀变化的时候,才会真正地请求资源,否则返回 304 并加载浏览器缓存。
网友评论