- 8-22 14:44 更新 – 补充修正错误 完事后的讨论 – 各种途径的访问
前言
做为一个正宗的科班出身, 《计算机网络》 这门课程肯定是学过的,但到底有没有学细学透学明白,就不得而知了。
依旧是做为一个正宗的科班出身伪前端,在新的互联网寒冬之下又被某互联网公司的面试官喷了。
虽然曾经还是比较仔细的看过 HTTP 相关的东西,但长时间没有复习加上当时没有做笔记、写博客沉淀还是有很多的生疏。
回去之后查了一下相关的资料,算是做一次知识的沉淀(造轮子)。
正文
1、 304
打开 Chrome
,按下 F12
、访问一个曾经访问过的网页,如果没有勾选 Disable cache
,很容易能看到这样的状态:
304,代表 NOT MODIFIED
,他发生在这样的一种状态下:服务器正确接收到了一个带条件(Conditional Validation)的 GET
,如果这个条件是真的就会返回 304
、否则就会返回 200
(A conditional GET or HEAD request has been received and would have resulted in a 200 OK response if it were not for the fact that the condition evaluated to false)。
换个角度来说,如果浏览器接收到的 response
的状态码是 304
,就代表这个资源在客户端中的缓存依然是有效的,即在上次拿到资源到当前这段时间之内服务器端并没有对这个资源做修改。
这样做有什么好处呢? 显然这样可以在使用很小的一个 HTTP
请求的代价上就实现下面两个功能:
- 确保拿到的资源是最新的
- 客户端资源没有问题的情况下不需要再次那资源,解决每次完整请求资源时的性能问题。 撒
那所谓的带提交的 GET
是怎么回事呢? 请看下面的内容:
2、 扑所迷离的 etag 与 last-modified
a、暧昧期的表白
在大多数的情况下大家听到的可能就是 etag
与 last-modified
,没错,他就是服务器在接收到带条件的 GET
请求之后塞到 response
的 header
里面。那条件是什么?是不是又与 etag
和 last-modified
相对应呢?
答案是肯定的,他们的对应如下所示:
etag —— if-none-match
last-modified —— if-modified-since
b、前戏
有了上面的对应关系之后相信不再扑朔了。他们的关系如下:
如果本地有相关资源的缓存,并且在缓存的时候响应头里面有 etag
或者 last-modified
的情况,这个时候去请求服务器的时候就会是带有条件的 GET
请求(Conditional Validation)。
在请求头里面可能会有两个字段: if-none-match
、 if-modified-since
,其中 if-none-match
的值是服务器上次返回该资源时响应头里面 etag
的值,if-modified-since
的值是服务器上次返回该资源时响应头里面 last-modified
里的值。
紧接着服务器端就会接收到这个带有条件的 request
,然后会根据这两个值去判断缓存的资源是否是最新的。
如果没问题,即资源是最新的情况下就会返回 304
,body
为空;不是的话就会返回 200
,即目前浏览器端的资源不是最新的,body
里面就是资源体,然后客户端就会用最新返回的资源覆盖掉之前的资源
也就是说。发送这种带条件的请求的必要条件是 资源在浏览器端有缓存,并且在缓存的时候服务器端的 reponse
里面有 etag
或者 last-modified
。如果这个条件不满足,发送的请求就是没有条件的(unconditionally)。
c、前戏后的协商 —— 一些优化
虽然说通过这种方式能够减轻服务器的压力,解决一些请求资源时的性能问题。但是细细看来,还是存在一些浪费:每个都要去带上条件请求服务器来看资源是不是最新的,大多情况下是最新的情况下岂不是每次都在做无意义的验证? 别急,做个约定就好,在 response
里面加上 Cache-Control
和 Expires
即可。
通常他们是长这样的:
cache-control:max-age=96247433
expires:Thu, 03 Jan 2019 04:24:16 GMT
Cache-control
用于控制HTTP缓存(在HTTP/1.0中可能部分没实现,仅仅实现了Pragma: no-cache)
;Expires
表示存在时间,允许客户端在这个时间之前不去检查(发请求),等同 max-age
的
效果。但是如果同时存在,则被 Cache-Control
的 max-age
覆盖。
d、前戏后的争执
这么多的字段,现在数数好像已经有 6 个了,那么他们是谁先谁后?
如果比较粗的说先后顺序应该是这样:
-
Cache-Control
—— 请求服务器之前 -
Expires
—— 请求服务器之前 -
If-None-Match (Etag)
—— 请求服务器 -
If-Modified-Since (Last-Modified)
—— 请求服务器
需要注意的是 如果同时有 etag
和 last-modified
存在,在发送请求的时候会一次性的发送给服务器,没有优先级,服务器会比较这两个信息(在具体实现上,大多数做法针对这种情况只会比对 etag
)。服务器在输出上,如果输出了 etag
就没有必要再输出 last-modified
(实际上大多数情况会都输出)。
具体可以参见知乎上的这个问题——关于浏览器的缓存,有了Etag
,last-Modified
还有必要存在吗???。如果要深究,就要仔细看看 RFC
了,如果恰好你看了,欢迎在评论中提出。
e、完事后的讨论 – 各种途径的访问
浏览器输入 url 之后敲下回车,刷新 F5 与强制刷新(Ctrl + F5),又有什么区别?
实际上浏览器输入 url 之后敲下回车就是先看本地 cache-control、expires 的情况,刷新(F5)就是忽略先看本地 cache-control、expires 的情况,带上条件 If-None-Match、If-Modified-Since,强制刷新(Ctrl + F5)就是不带条件的访问。
值得注意的是,如果是 浏览器输入 url 之后敲下回车
你在 network
里面看到的状态往往是 200
,但是大小是 0。这是因为这个 200 是上次访问资源返回的状态码。
如果你是一位开发者,还是建议在 Chrome
里面开启 Disable Cache
.
推荐阅读资料
- 304 NOT MODIFIED
- Understanding HTTP 304 Responses
- HTTP头的Expires与Cache-control
- Caching Improvements in Internet Explorer 9
捐赠
写文不易,赠我一杯咖啡增强一下感情可好?