2

为了优化性能,使用缓存是一种比较常见的手段。那么如何实现缓存以及如何避免缓存呢,都是要探讨的话题。可以从三个部分:http 缓存、cookie、localStorage&sessionStorage 来重点讲述缓存实现的原理、过程以及实现的方式。由于篇幅原因,本篇重点讲述 http 缓存。

1.基本概念

缓存命中: 使用已有的副本为到达的请求提供资源而不用从服务器中获取资源。 
缓存未命中: 达缓存的请求没有副本可用,而被转发给原始服务器,与缓存命中相反。 
CHP: Cache Hit Percentage,缓存提供服务请求所占有的比例,缓存的文件个数/请求资源个数。 
下图是打开百度资源后所请求的资源(部分)情况:
clipboard.png

可以看到其中有两个资源是从服务器中获取的,其余是从缓存中获取,那么其CHP值为:80%。

2.强缓存

所谓的强缓存是指请求资源的时候不需要发送 http 向服务器发送请求,直接从客户端获取资源。实现的方式是有 http 的 Expires,Cache-Control两个response header实现的。

2.1 Expires

Expires是 http 1.0 提出的一个 header,其值是一个资源有效期的绝对时间,实现缓存的过程如下: 

  • 浏览器发送请求,服务器在response 的header中加入Expires,并将资源返回给浏览器; 
  • 浏览器获取到资源后,会存储一个资源的副本,并将EXpires中的时间进行保存; 
  • 当再次进行资源请求的时候,浏览器会将当前的请求时间与之前存储的时间进行比较; 
  • 如果请求的时间在有效期之内,则直接使用副本,反之会向服务器发送资源并且重新更新资源的有效期

Expires的值为一个绝对时间,当客户端改变了时间或者时区问题,会导致缓存失效;因此在http 1.1 中引入了Cache-Control。

Cache-Control与Expires同时存在的时候Cache-Control的优先级大于Expires

2.2 Cache-Control

Cache-Control 实现的过程与 Expires 类似,其常见的有三个值: 

  • max-age: 以秒为单位,表示资源缓存的相对时间。浏览器再次进行请求的时候,会将上次请求的时间与max-age相对时间求和,在与现在请求的时间相比较,如果在计算的时间范围内,则缓存可用; 
  • no-cache: 表示资源可以进行缓存,但是在下次进行使用的时候,必须去服务器端进行再验证。如果返回的response status 是304,那么就是从缓存中获取资源,如果是200 则从服务器端获取资源; 
  • no-store: 资源不能进行本地缓存,再次请求资源的时候,需要去服务器重新获取资源。

Cache-Control:max-ag=0 与 Cache-Control:no-cache 在使用效果上没有多大的区别,区别在于服务器挂了是否可以使用之前的缓存的response 即 max-age=0 可以看到之前缓存的内容,页面正常显示之前的内容而 no-cache 则返回错误5xx状态码

2.3 如何加入Expires与Cache-Control头部

上面讲述利用Expires与Cache-Control如何实现缓存的,那怎么样在请求资源的时候加入这两个header呢,有以下两种方式: 
(1) 通过代码的方式,在web服务器返回的响应中添加响应头部,如在 Express 框架中使用 setHeader 加入,代码如下:

const picMap = {
 'logo.png': 'no-store',
 'avatar.png': 'no-cache',
 'background.png': 'max-age: 36000'
}
app.use(express.static(publicPath, setHeaders(res, filePath, stat) {
 let baseName = path.basename(filePath);
 picMap[baseName] && res.set('Cache-Control', picMap[baseName])
}));

(2) 通过配置web服务器的方式,在服务器配置文件中加入 Expires 与 Cache-Control,进行统一配置。

3.协商缓存

协商缓存是在用户强缓存失败的情况下,向服务器端进行再验证。它与强缓存的区别在于会向服务器发送请求,但是不会获取资源,浏览器端请求的资源还是从缓存中获取。其实现有两对首部:【Last-Modified,If-Modified-Since】、【ETag、If-None-Match】其中以【Last-Modified,If-Modified-Since】为例,讲解实现的过程。

  • 浏览器第一次请求一个资源,服务器会将资源以及资源的最后修改时间(时间放入Last-Modified)发送给浏览器;
  • 浏览器获取资源,将资源以及修改时间进行保存;
  • 浏览器再次请求相同资源的同时,在请求的头部加入 If-Modified-Since,其值为上一次请求保存的时间;
  • 服务器会将资源最后修改的时间与 If-Modified-Since 相比较,如果时间一致就返回 304 状态码,反之则发送新的资源给服务器。

4.缓存避免

开发侧
(1) 给资源加上一个动态的参数,如css/index.css?v=0.1 
(2) 如果缓存问题出现在 ajax 请求中,可以给请求地址添加随机数;

用户侧
(1) F5:cache-control:max-age=0 
(2) Ctrl+F5:请求的时候不带上任何缓存头

参考文献
[1] 《http权威指南》。 
[2] https://www.cnblogs.com/lyzg/...


前端知知
53 声望2 粉丝