浏览器缓存机制

缓存在计算机领域可以说是一个非常重要的提升性能的思想

CPU 有缓存, 内存也有缓存, 网络当然也能有缓存.

HTTP 缓存的意义在于: 减少直接访问资源, 可以让获取资源的速度更快, 同时也减小了直接访问资源带来的性能和网络流量的开销

缓存控制

HTTP缓存是通过服务器在响应资源的报文头中添加首部字段实现的

cache-control

Cache-Control 通用消息头被用于在 http 请求和响应中通过指定指令来实现缓存机制.

完全不缓存

每次响应都会下载完整的资源

Cache-Control: no-store  

不缓存内容

以下这no-cache值会在缓存释放前请求服务器验证资源是否有效

Cache-Control: no-cache  

私有缓存和公共缓存

public:响应可以被任何请求来源缓存。

private:响应的内容只能被唯一的用户缓存,不可以被共享缓存存储。

Cache-Control: private  
Cache-Control: public  

缓存过期

max-age 是距离请求发起的时间的秒数。一般来说可以给不经常修改的静态资源设置这个值.

Cache-Control: max-age=31536000  

缓存验证

must-revalidate在使用一些老的资源前强制验证状态判断其是否过期.

Cache-Control: must-revalidate  

Expires

Expires 头指定了一个日期, 在这个日期之后,资源被认为是过期的;

expires: Fri, 19 May 2017 11:45:54 GMT  

Last-Modified

Last-Modified定义资源上次被修改的时间.

Last-Modified:Mon, 06 Feb 2017 16:15:51 GMT  

缓存的时间控制

可以看出 ExpiresCache-Control: max-age, Last-Modified 是用于控制缓存的时间的

cache-control: max-age 的优先级是高于expires的, 在两者都存在的情况下, 浏览器会忽略 expires 首部.

cache-controlexpires 都不存在时, 会查找Last-modified字段, 当这个字段存在时, 这个资源缓存的寿命就等于头里面 Date(Date 字段表明 HTTP 请求被报文被创建的时间) 的值减去 Last-Modified 的值除以 10.

需要说明的是: HTTP 资源在没有过期并且不被抛弃的情况下是不会重新请求的

在这里, 资源被抛弃的情况有:

  1. 用户手动删除或者浏览器定期移除
  2. Ctrl+F5强制刷新

缓存的校验

在资源过期或者用户刷新(刷新按钮, F5, ctrl+r)的情况下, 浏览器会进行缓存校验或者重新获取.

只有在响应包含缓存检验头的情况下, 浏览器才会进行缓存校验.

缓存检验头有EtagsLast-Modified

Etag

Etag 是用于对一个资源的唯一标识, 它可以是任意字符串, 它可以是一个时间, 一个版本号, 或者对于资源的信息摘要(md5, sha1), 当资源被修改或者替换时, 服务器会对这个资源生成一个Etag.

浏览器需要校验资源是否过期时, 会带上这个资源之前的 HTTP 响应里的 Etag 字段信息放到 If-None-Match字段中请求验证, 服务器拿到 If-None-Match 的值, 如果这个值跟服务器上这个资源的 Etag 值相同, 那么返回304Not Modified 否则返回200 ok同时包新的资源

🌰:

某响应:

Etag: "686897696a7c876b7e"  

浏览器请求验证:

If-None-Match: "686897696a7c876b7e"  

服务器校验:

// 返回 304
服务器资源的 Etag: "686897696a7c876b7e" == 浏览器请求的If-None-Match: "686897696a7c876b7e" 

// 返回200 + 资源
服务器资源的 Etag: "f4da5f4ds8g43da1g3" != 浏览器请求的If-None-Match: "686897696a7c876b7e" 

Last-Modified

Last-Modified定义资源上次被修改的时间.

校验时, 浏览器在请求中把 Last-Modified的值放到If-Modified-Since中验证, 如果服务器检查到这个资源被修改了就和上面一样, 返回304同时可以更新过期时间, 否则返回200并包含响应实体.

🌰:

某响应:

Last-Modified: Mon, 06 Feb 2017 16:15:51 GMT  

浏览器请求验证:

If-Modified-Since: Mon, 06 Feb 2017 16:15:51 GMT  

服务器校验:

// 返回 304
服务器资源的上次修改时间: Mon, 06 Feb 2017 16:15:51 GMT == 浏览器请求的If-Modified-Since: Mon, 06 Feb 2017 16:15:51 GMT

// 返回200 + 资源
服务器资源的上次修改时间: Fri, 19 May 2017 11:45:54 GMT != 浏览器请求的If-Modified-Since: Mon, 06 Feb 2017 16:15:51 GMT

以上两种检验机制其实是差不多的, 都是发送请求给服务器校验查询资源是否被修改, 如果被修改, 返回新的资源, 如果没有被修改, 返回304.

但是这两种校验机制还是有略微的不同并各有优劣:

  1. Etag 是强检验机制, Last-Modified 是弱校验机制, 也就是 Etag 可以在后续所有的请求用使用, 而 Last-Modified 只会在后续的一次请求中使用.
  2. Etag 比Last-Modified 在时间上更精确, 因为 Last-Modified 的时间是精确到秒的.
  3. 某些服务器没有办法准确计算资源的 Last-Modified 值
  4. 计算Etag耗费的性能相比 Last-Modified 要大

最后从 AlloyTeam 那偷来的流程图

浏览器缓存机制

参考

Etag wiki

MDN HTTP缓存

浅谈 Web 缓存

-- Ming