写作背景
- 在网络上,介绍信息类,传递资讯类的文章有很多,但真正让读者理解的文章却很少。
- 笔者想做的,是结合自己的理解,将资讯以通俗易懂的方式表达出来。
- 让读者不仅仅了解该项技术,更理解其原理,清楚其流程,真正掌握它。
笔者技术水平有限,文章可能有错误,遗漏的地方。敬请指导,期待与您的交流。
缓存状态码
200
- 运行流程:【浏览器】--(200 form cache)--》【浏览器缓存】--》【页面展示】
- 浏览器不需要向服务器发起请求,而是直接调用浏览器缓存的页面进行展示。
304
- 运行流程:【浏览器】--》【服务器】--(304)--》【浏览器】--》【浏览器缓存】--》【页面展示】
- 浏览器向服务器发起请求,并在请求头中携带该页面的特征值,如「最后修改时间」,「页面内容 hash 值」等。
服务器接收到请求,对比请求头中携带的特征值,与服务器中页面的特征值是否一致。如果特征值没有变化,则认为该页面内容未发生变化,只返回 304 状态码的响应头,响应体为空。
- 一般而言,这部分逻辑,是不需要开发者手动实现的,是框架自带的。
- 当然,开发者也可以通过某些设置关闭该功能,即直接返回服务器中的内容,不管与浏览器中缓存的是不是一样。
- 浏览器接收到 304 响应头,调用浏览器缓存中的页面,进行内容展示。
什么情况下出现 304,什么时候出现 200 form cache ?
对于缓存过的页面,该页面的「URL的直接资源」会进行 304 处理,页面中需要加载的资源将进行 200 form cache 处理
譬如
http://www.mongoosejs.net/docs/schematypes.html
这个页面。
http://www.mongoosejs.net/docs/schematypes.html
直接指向的 html, 即该页面的「URL的直接资源」,会进行 304 处理。除了接口以外,该 html 中需要加载的「其他资源」,如 css,js,图片等资源,都属于 200 form cache 资源。无论该资源是通过 html 中的标签载入,还是通过 js 代码,动态载入。都属于「其他资源」。
为什么这样设计呢?
- 因为该页面的「URL的直接资源」,是该页面的首个资源,需要访问服务器确定该页面是否有更新,需要给服务器更新资源的机会。如果个资源都不访问服务器,不与服务器沟通,那就无法得知服务器是否有资源更新。所以无论如何设置,调用页面的首个资源都是应当访问服务器的。
- 而对于「其他资源」,无论「URL的直接资源」是否有变化,即使「URL的直接资源」 返回的不是 304,而是全新的内容,只要里面描述的「其他资源」对应 URL 没有变化。那浏览器在一定时间内(什么时间,后面会介绍到),都会直接调用缓存中的文件, 200 form cache。
- 如果「URL的直接资源」(html)描述的「其他资源」(如,css,js)的 URL 发送了变化,浏览器就会当做新的资源文件,从服务器中重新获取(注意,是当做新资源获取,而不是判断是否更新的 304。因为是当做新的资源,而不是改变后的资源)。
为什么「其他资源」不像「URL的直接资源」那样,访问服务器判断更新?不访问怎么知道有没有更新呢?
- 为什么不访问服务器,因为这类资源的量一般比较多,即使内容没有发生变化,只返回 304,也需要经过 TCP 的三次握手和四次挥手。而且浏览器有同一域名的并发访问数量限制,都是需要消耗时间的。一旦资源大,这个时间成本就不可忽略了。所以不访问服务器,直接从浏览器缓存取。
- 如果这些资源真的有变化怎么办?修改其对应的链接,把他当做新资源访问,通过修改「URL的直接资源」中的代码。哪怕是在后面加些参数,如加个时间串
?v=20191120122038
。浏览器也会当做新的资源。从服务器加载。
- 所以即使你「其他资源」内容变化了,「URL的直接资源」没有改变(导致 html 中 css 的 URL 没有变化),对应的「其他资源」都直接调用缓存中的内容,不经过服务器,这也是前端同学经常遇到的,无法显示最新效果的「缓存问题」。
- 那是不是只有我不修改链接,就永远都不重新访问资源了呢?不是的,资源会有过期时间,如果超过了过期时间,就会重新访问服务器了。下一节会对其进行介绍。
各缓存头(请求头,响应头)
强缓存
服务端通过响应头告诉浏览器,「接下来的一段时间」内,直接使用缓存内容。
- 有没有觉得很熟悉?没错,这就是应用在「其他资源」中的技术。为什么访问「其他资源」时,不链接服务器,直接从浏览器缓存拿。因为这是服务器告诉浏览器的,也就是说,是服务端让浏览器从缓存中取的。不是浏览器自作主张设置的。
- 那么「接下来的一段时间」是指那段时间呢?这是由服务器通过响应头告诉浏览器的。具体用到的响应头如下:
(http1.0)
- Expires:标识该资源过期的绝对时间,如
Thu, 21 Nov 2019 00:10:44 GMT
,这表明该资源于 2019年11月21日 00时11分44秒过期。在该时间点之前访问,则让浏览器直接从缓存上取。
- Expires:标识该资源过期的绝对时间,如
(http1.1)
Cache-Control:接受多个参数,其中一个参数是定义过期时间。如
Cache-Control: public, max-age=31536000
,其中max-age=31536000
就是告知浏览器,在接收到该文件那一刻算起的 31536000 秒内有效,直接从浏览器缓存上取。
协商缓存
浏览器会向服务端发起http请求,然后服务端告诉浏览器文件未改变,让浏览器使用本地缓存。使用 Ctrl+F5强制刷新可以使得缓存无效
- 是的,这就是「URL的直接资源」应用的技术,协商缓存返回的就是 304 状态码。具体用到的响应头,请求头如下:
(http1.0)
- Last-Modified/If-Modified-Since
- 其中,在首次获得资源时,服务端携带的相应头是 Last-Modified,告诉浏览器该文件的最后修改时间。
- 在浏览器再次发起请求时,就会将 Last-Modified 的值,携带到请求头中,并以 If-Modified-Since 为 key 保存。
- 服务器在接收到 Last-Modified 请求头后,会与本地文件的修改时间进行比对,判断文件是否有变化,如果没有变化,则返回 304 状态码。
(http1.1)E-tag/If-None-Match
- 与 Last-Modified/If-Modified-Since 类似,只是判断的方式不同,Last-Modified/If-Modified-Since 是用修改时间进行判断,If-None-Match/E-tag 则是通过文件内容的 hash 进行判断。此判断标准更加彻底,准确。
- 在首次获得资源时,服务端携带的响应头是 E-tag。告诉浏览器该文件内容的 hash 值。
- 在浏览器再次发起请求时,将 E-tag 的值,携带到请求头中,并以 If-Modified-Since 为 key 保存。
- 服务器在接收到 If-Modified-Since 请求头后,会与本地文件的 hash 值进行比对,判断文件是否有变化,如果没有变化,则返回 304 状态码。
如果同时带有E-tag和Last-Modified,服务端会优先检查E-tag。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。