欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

浏览器缓存机制

程序员文章站 2024-01-28 09:28:58
本文整理在,我的 "github" 上。要是对您有帮助,就点歌星星呗。我会持续更新的。 关于3xx系列的状态码 300 多资源选择 301 永久重定向 302 临时重定向 303 告诉用户,使用另一个url获取资源 主要目的是允许POST请求的响应将客户端定位到某个资源上。比如说,在文件上传完成后让 ......

本文整理在,我的github上。要是对您有帮助,就点歌星星呗。我会持续更新的。

关于3xx系列的状态码

  • 300 多资源选择
  • 301 永久重定向
  • 302 临时重定向
  • 303 告诉用户,使用另一个url获取资源

    主要目的是允许post请求的响应将客户端定位到某个资源上。比如说,在文件上传完成后让客户端自动重定向到一个上传成功的结果页面。

  • 307 通知用户临时从其他url响应,重定向的页面展示给用户,让用户去点重定向url链接。
  • 304 服务端没有变更

    通知浏览器,可以使用缓存。这篇博文要描述的重点

浏览器(http)缓存机制

类型

说到浏览器缓存,通常会遇到一下两种。

  • 200(from cache)
  • 304

那么,这两种http状态,对应什么样的机制,流程呢?

缓存机制

综合以上描述,浏览器在请求一个资源时,使用缓存的流程大概如下。

  1. 首先浏览器会判断,这个资源是否有缓存。没有的话,正常请求。
  2. 如果有缓存的话,浏览会判断缓存是否过期。如果缓存没有过期,则直接使用。此时就是 200(from cache)

    通过上次缓存留下的:cache-control max-age 和 expires。
    注意:cache-control的优先级高于expires。

  3. 如果浏览器的缓存过期了,它会请求服务器。服务器会校验缓存的数据是否真的发生了更改。如果服务器端发现数据没有变。就会返回一个304告诉浏览器,你请求的数据 “not modified”,可以继续用缓存。同时,浏览器会更新缓存首部的过期时间等信息。

    这里浏览器发起请求时,会用到上次缓存首部的last-modified/e-tag。
    具体做法是:
    取出上次缓存的last-modified的值,放在本次请求header的 if-modified-since 中。
    取出上次缓存的e-tag的值,放在本次请求header中的 if-none-match 中。
    服务器会据此判断资源是否发生过修改,浏览器中的缓存是否依然可用。

  4. 如果服务器端修改了上次缓存的内容,则直接返回200,并携带新的内容。

浏览器缓存机制

相关http头

上文中提到了几个http头,这里做一下详细的解释。

cache-control

  • public: 所有内容都将被缓存
  • private: 内容只缓存到似有缓存中
  • no-cache: 所有内容都不会被缓存
  • no-store: 所有内容都不会被缓存到缓存或者internet临时文件中
  • must-revalidation/proxy-revalidation: 如果缓存的内容失效,请求必须发送到服务器/代理以进行重新验证(主要用于控制浏览器的前进、后退)
  • max-age=xxx( xxx is numeric ): 缓存的内容将在 xxx 秒后失效, 这个选项只在http 1.1可用, 并如果和last-modified一起使用时, 优先级较高

    expires

    头部字段提供一个日期和时间。
    (cache-control max-age 和 s-maxage 将覆盖 expires 头部。)

    last-modified/e-tag

  • 若服务器在响应一个资源时添加了last-modified字段,那么当下一次浏览器再一次向服务器请求该资源时(前提是浏览器中上一次的资源被缓存过了),会在请求header中包含if-modified-since字段,且值与服务器第一次响应给浏览器的last-modified字段一致。

  • 若服务器在响应一个资源时添加了etag字段,那么当下一次浏览器再一次向服务器请求该资源时(前提是浏览器中上一次的资源被缓存过了),会在请求header中包含if-none-match字段,且值与服务器第一次响应给浏览器的etag字段一致
    那么上述是遵循了http协议的浏览器会自动实现的,而要实现304的功能,就需要服务器(比如apache对于静态资源会自动实现这两个字段的响应)或者我们手动在服务器端编写响应的逻辑来实现。

写到这,整个浏览器及http缓存机制应该彻底介绍清楚了。
那么,就只剩下如何应用了。

原理与应用

静态资源

所谓静态资源,就是很少会修改的内容,包括前端常年打交道的css、js及图片文件等等。
通常情况下,静态资源的http头是这样配置的。

cache-control: public, max-age=31536000
expires: (一年后的今天)
etag: (基于内容生成)
last-modified: (过去某个时间)
vary: accept-encoding

到这里,可以据此给站点中全部的静态资源配置缓存头。这里我有两个建议。

  • 建议给每个静态资源路径添加指纹信息。可以使用gulp或者webpack等构建工具。通过不同版本的指纹信息控制更新缓存。
  • 项目中引用的依赖,建议使用cdn资源。依据缓存原理,浏览器只要访问过相同cdn的资源,就会留下缓存,哪怕其他站点。

动态资源

基础配置

对于非私密性和经常性变动的资源,我们可以这样做。

cache-control: public, max-age=0
expires: (当前时间)
etag: (基于内容生成)
last-modified: (过去某个时间)
vary: accept-encoding

这样配置的意义在于,内容可以被浏览器缓存起来。但是因为过期时间的问题,每次都会到服务端判断是否过期。未过期的话,就会收到304,进而继续使用缓存。

控制返回、前进

你如果需要更严格的控制,需要告知浏览器即使当用户点击了「返回/前进」按钮,也需要重新检查这些资源文件,那么可以使用:

cache-control: public, no-cache, no-store

私密性缓存

对于私密或者针对用户的内容,需要把 public 替换为 private 以避免内容被代理缓存。

cache-control: private, …

关于etag

建议避免使用 etag。尤其是使用apache,又配置了负载均衡的情况下。

针对生成的 etag,默认的apache方法需要把文件的索引节(inode),大小(size)和最后修改时间作为输入求值得到。这会导致在负载均衡的环境中,生成的 etag 值变得毫无用处,因为每个服务器都会针对相同的文件生成一个不同的 etag 值。这个可能就是唯一的问题导致很多人完全禁用 etag,其实只要精确地针对一个匹配的文件生成一个独一无二的 etag 值,就没有必要禁用 etag 了。

vary: accept-encoding

指定“vary: accept-encoding”标头,用一句话来说明它的意义,就是“告诉代理服务器缓存两种版本的资源:压缩和非压缩,这有助于避免一些公共代理不能正确地检测content-encoding标头的问题。
还有一个现实的原因:ie 浏览器不缓存任何带有 vary 头但值不为 accept-encoding 和 user-agent 的资源。所以通过这种方式添加这个头,才能确保这些资源在 ie 下被缓存。
总的来说,加这么一句肯定没错,相当于“多喝热水”。