Profession & Efficient

保持对技术的敬畏之心

通俗易懂的浏览器缓存讲解

《通俗易懂的浏览器缓存讲解》

写作背景

  • 在网络上,介绍信息类,传递资讯类的文章有很多,但真正让读者理解的文章却很少。
  • 笔者想做的,是结合自己的理解,将资讯以通俗易懂的方式表达出来。
  • 让读者不仅仅了解该项技术,更理解其原理,清楚其流程,真正掌握它。
  • 笔者技术水平有限,文章可能有错误,遗漏的地方。敬请指导,期待与您的交流。

    《通俗易懂的浏览器缓存讲解》

缓存状态码

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秒过期。在该时间点之前访问,则让浏览器直接从缓存上取。
  • (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。

    《通俗易懂的浏览器缓存讲解》

讲完了

《通俗易懂的浏览器缓存讲解》

点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注