携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第10天,点击查看活动详情
什么是web缓存
web缓存主要分为http缓存和浏览器缓存
浏览器缓存
浏览器缓存主要有:localStorage、sessionStorage、cookie 等,这些都是浏览器缓存,主要用于存储一些可以暴露的数据以及传到后台的参数
主要缺点:浏览器缓存的这些方法,能保存的数据大小都有限制
http缓存
Web 缓存是可以自动保存常见文档副本的 HTTP 设备。当 Web 请求抵达缓存时, 如果本地有“已缓存的”副本,就可以从本地存储设备而不是原始服务器中提取这 个文档。
服务器需要接受并处理http请求,并且通过http区传输数据,这些都是需要带宽的,而带宽的增加会增加成本的投入,http缓存就是让服务器不去处理这个请求,客户端也能拿到数据
注:http缓存主要针对的是html、css、img等的静态资源,通常不会去缓存一些动态的资源,因为缓存动态资源的话数据的实时性就不会太好
优点:
减少不必要的网络传输,节约带宽,目的就是省钱
加速页面加载
减少服务器负载,避免服务器过载
缺点:
占内存:部分缓存会被存放到内存中
http缓存分为两种缓存:强制缓存和协商缓存
当浏览器请求服务器的某项资源(A)时, 服务器根据A算出一个哈希值(3f80f-1b6-3e1cb03b)并通过 ETag 返回给浏览器,浏览器把"3f80f-1b6-3e1cb03b" 和 A 同时缓存在本地,当下次再次向服务器请求A时,会通过类似 If-None-Match: "3f80f-1b6-3e1cb03b" 的请求头把ETag发送给服务器,服务器再次计算A的哈希值并和浏览器返回的值做比较,如果发现A发生了变化就把A返回给浏览器(200),如果发现A没有变化就给浏览器返回一个304未修改。
强制缓存
Expires和Cach-control
Expires:
机制是:获取本地时间戳,并对先前拿到的资源文件中的
Expires字段的时间做比较。来判断是否需要对服务器发起请求。
cach-control:
//往响应头中写入需要缓存的时间
res.writeHead(200,{
'Cache-Control':'max-age=10'
});
max-age:单位是秒,缓存时间计算的方式是距离发起的时间的秒数,超过间隔的秒数缓存失效
s-maxage:决定代理服务器缓存的时长(结合public属性使用)
这两个属性的值就是设置缓存保存的时长,在设置的时长时间段里面重复请求缓存中的资源就不会去发生服务器响应,而是直接加载缓存中的资源,这也可以理解为强缓存;而当设置的时长失效后,就会去触发协商缓存重新去服务器发生请求响应
这两个方法并不互斥,可以一起使用,通常的项目架构中使用max-age就够用,s-maxage根据业务需求来决定
no-cache:不使用强缓存, 强制进行协商缓存
并不是字面意思上的不使用强缓存,而是强制进行协商缓存
会直接跳过强缓存校验,直接进行协商缓存
no-store:禁止使用任何缓存(包括协商缓存),每次都向服务器请求最新的资源
private:专用于个人的缓存(浏览器),中间代理、CDN 等不能缓存此响应
public:响应可以被中间代理、CDN 、浏览器等缓存
如果没有设置public和private,则默认为private
注:这两个是互斥属性,不能同时出现在响应头 cache-control字段 中
must-revalidate:在缓存过期前可以使用,过期后必须向服务器验证
Pragma:只有一个属性,就是no-cache,作用和cach-control中的no-cache作用基本一样,但是它的优先级是最高的
协商缓存
当浏览器的强缓存失效的时候或者请求头中设置了不走强缓存,并且在请求头中设置了If-Modified-Since 或者 If-None-Match 的时候,会将这两个属性值到服务端去验证是否命中协商缓存,如果命中了协商缓存,会返回 304 状态,加载浏览器缓存,并且响应头会设置 Last-Modified 或者 ETag 属性。
协商缓存有两种方法
Etag / if - None - Match
Last - Modified / if - Modified - Since
last-modified的协商缓存实现方式如下,这三步缺一不可:
- 首先需要在服务器端读出文件修改时间,
- 将读出来的修改时间赋给响应头的
last-modified字段。- 最后设置
Cache-control:no-cache
机制
在第一次向服务器请求资源时,将文件修改时间赋值到相应头中, 第二次发起请求的时候,请求头会带上上一次响应头中的 Last-Modified 的时间,并放到 If-Modified-Since 请求头属性中,服务端根据文件最后一次修改时间和 If-Modified-Since 的值进行比较,如果相等,返回 304 ,并加载浏览器缓存;如果不等就会重新请求响应修改后的资源
last-modified也有缺点
- 因为是更具文件修改时间来判断的,所以,在文件内容本身不修改的情况下,依然有可能更新文件修改时间(比如修改文件名再改回来),这样,就有可能文件内容明明没有修改,但是缓存依然失效了。
- 当文件在极短时间内完成修改的时候(比如几百毫秒)。因为文件修改时间记录的最小单位是秒,所以,如果文件在几百毫秒内完成修改的话,文件修改时间不会改变,这样,即使文件内容修改了,依然不会 返回新的文件
而Etag则解决了上面的问题
Etag 将原先协商缓存的比较时间戳的形式修改成了比较文件指纹(指服务器根据文件内容计算出的唯一的哈希值,当文件内容被改变时哈希值就会改变)
其流程基本和last - modified一样,与之不同的是,Etag携带的值是文件指纹,而不是时间戳
需要注意的是,Etag是有强弱校验之分的
当哈希码的前缀是 “W / ” 开头的一串字符串时,此时的协商缓存就是弱校验模式。只有当服务器上的文件差异达到能触发哈希值后缀变化时才会去服务器请求资源; 弱验证是提取文件的部分属性来生成哈希值。因为不必精确到每个字节,所以他的整体速度会比强验证快,但是准确率不高。会降低协商缓存的有效性
反之强校验则是只要发生修改就会发生哈希值修改并去服务器请求资源, 它可以保证文件内容绝对的不变。但是,强验证非常消耗计算量
注:值得注意的一点是,不同于cache-control是expires的完全替代方案(能用cache-control就不要用expiress)。ETag并不是last-modified的完全替代方案。而是last-modified的补充方案(简单说就是项目中到底是用ETag还是last-modified完全取决于业务场景,这两个没有谁更好谁更坏)。
总结
- 有些缓存是从磁盘读取,有些缓存是从内存读取,有什么区别?答:从内存读取的缓存更快。
- 所有带304的资源都是协商缓存,所有标注(从内存中读取/从磁盘中读取)的资源都是强缓存。
- http缓存可以减少宽带流量,加快响应速度。
- 关于强缓存,
cache-control是Expires的完全替代方案,在可以使用cache-control的情况下不要使用expires - 关于协商缓存,
etag并不是last-modified的完全替代方案,而是补充方案,具体用哪一个,取决于业务场景。 - 协商缓存和强缓存的区别仅在于是否需要服务器端校验。最后都是去内存/硬盘中获取的。