小小狐狸 2019-07-01
为了优化性能,使用缓存是一种比较常见的手段。那么如何实现缓存以及如何避免缓存呢,都是要探讨的话题。可以从三个部分:http 缓存、cookie、localStorage&sessionStorage 来重点讲述缓存实现的原理、过程以及实现的方式。由于篇幅原因,本篇重点讲述 http 缓存。
缓存命中: 使用已有的副本为到达的请求提供资源而不用从服务器中获取资源。
缓存未命中: 达缓存的请求没有副本可用,而被转发给原始服务器,与缓存命中相反。
CHP: Cache Hit Percentage,缓存提供服务请求所占有的比例,缓存的文件个数/请求资源个数。
下图是打开百度资源后所请求的资源(部分)情况:
可以看到其中有两个资源是从服务器中获取的,其余是从缓存中获取,那么其CHP值为:80%。
所谓的强缓存是指请求资源的时候不需要发送 http 向服务器发送请求,直接从客户端获取资源。实现的方式是有 http 的 Expires,Cache-Control两个response header实现的。
Expires是 http 1.0 提出的一个 header,其值是一个资源有效期的绝对时间,实现缓存的过程如下:
Expires的值为一个绝对时间,当客户端改变了时间或者时区问题,会导致缓存失效;因此在http 1.1 中引入了Cache-Control。
Cache-Control与Expires同时存在的时候Cache-Control的优先级大于Expires
Cache-Control 实现的过程与 Expires 类似,其常见的有三个值:
Cache-Control:max-ag=0 与 Cache-Control:no-cache 在使用效果上没有多大的区别,区别在于服务器挂了是否可以使用之前的缓存的response 即 max-age=0 可以看到之前缓存的内容,页面正常显示之前的内容而 no-cache 则返回错误5xx状态码
上面讲述利用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,进行统一配置。
协商缓存是在用户强缓存失败的情况下,向服务器端进行再验证。它与强缓存的区别在于会向服务器发送请求,但是不会获取资源,浏览器端请求的资源还是从缓存中获取。其实现有两对首部:【Last-Modified,If-Modified-Since】、【ETag、If-None-Match】其中以【Last-Modified,If-Modified-Since】为例,讲解实现的过程。
开发侧
(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/...