技术点:前端缓存(web 缓存)详解

前端缓存(web 缓存)详解

什么是 web 缓存

web 缓存主要指的是两部分:浏览器缓存和 http 缓存

浏览器缓存: localStorage,sessionStorage,cookie 等等
http 缓存: 通过配置 http 请求头来达到缓存的效果,分为强缓存和弱缓存

缓存可以解决什么问题?他的缺点是什么?

解决的问题

  • 减少不必要的网络传输,节约宽带(就是省钱)
  • 更快的加载页面(就是加速)
  • 减少服务器负载,避免服务器过载的情况出现。(就是减载)

缺点

  • 占内存(有些缓存会被存到内存中)

浏览器缓存

太简单了,略

http 强缓存

http 强缓存有两种方式,Expires 和 Cache-control

基于 Expires 实现的强缓存(已弃用)

Expires 字段的作用是,设定一个强缓存时间。在此时间范围内,则从内存(或磁盘)中读取缓存返回。比如说将某一资源设置响应头为:Expires:new Date(“2022-7-30 23:59:59”);那么,该资源在 2022-7-30 23:59:59 之前,都会去本地的磁盘(或内存)中读取,不会去服务器请求

res.writeHead(200, {
  Expires: new Date("2022-07-30 14:05:55").toUTCString(),
});

缺点: 因为 Expires 判断强缓存是否过期的机制是:获取本地时间戳,所以如果本地时间不对的话会出问题,会导致资源无法被缓存或者资源永远被缓存的情况

基于 Cache-control 实现的强缓存(代替 Expires 的强缓存实现方法)

Cache-control 这个字段在 http1.1 中被增加,Cache-control 完美解决了 Expires 本地时间和服务器时间不同步的问题。是当下的项目中实现强缓存的最常规方法。

//往响应头中写入需要缓存的时间
res.writeHead(200, {
  "Cache-Control": "max-age=10",
});

Cache-control 有 max-age、s-maxage、no-cache、no-store、private、public 这六个属性。

  • max-age 决定客户端资源被缓存多久。
  • s-maxage 决定代理服务器缓存的时长。
  • no-cache 表示是强制进行协商缓存。
  • no-store 是表示禁止任何缓存策略。
  • public 表示资源即可以被浏览器缓存也可以被代理服务器缓存。
  • private 表示资源只能被浏览器缓存。

设置多个值,使用逗号分割,

Cache-control:max-age=10000,s-maxage=200000,public

强制缓存就是以上这两种方法了。现在我们回过头来聊聊,Expires 难道就一点用都没有了吗?也不是,虽然 Cache-control 是 Expires 的完全替代品,但是如果要考虑向下兼容的话,在 Cache-control 不支持的时候,还是要使用 Expires,这也是我们当前使用的这个属性的唯一理由

http 协商缓存

基于 last-modified 的协商缓存
实现方式
  1. 首先需要在服务器端读出文件修改时间,
  2. 将读出来的修改时间赋给响应头的 last-modified 字段。
  3. 最后设置 Cache-control:no-cache
原理
  1. 服务端接收请求时,读取文件最后一次的修改时间,返回给客户端
  2. 客户端接收到之后,下次再调用该资源时会在请求头中添加 If-Modified-Since(就是服务器第一次修改时候给他的时间)
  3. 服务端拿到这个时间并再次读取该资源的修改时间,让他们两个做一个比对来决定是读取缓存还是返回新的资源
缺点

使用以上方式的协商缓存已经存在两个非常明显的漏洞。这两个漏洞都是基于文件是通过比较修改时间来判断是否更改而产生的。

  1. 因为是更具文件修改时间来判断的,所以,在文件内容本身不修改的情况下,依然有可能更新文件修改时间(比如修改文件名再改回来),这样,就有可能文件内容明明没有修改,但是缓存依然失效了。
  2. 当文件在极短时间内完成修改的时候(比如几百毫秒)。因为文件修改时间记录的最小单位是秒,所以,如果文件在几百毫秒内完成修改的话,文件修改时间不会改变,这样,即使文件内容修改了,依然不会返回新的文件。
基础 ETag 的协商缓存
实现方式
  1. 首先需要在服务器端读出文件哈希值
  2. 将读出来的哈希值赋给响应头的 etag 字段。
  3. 最后设置 Cache-control:no-cache
原理
  1. 服务端接收请求时,读取文件生成的哈希值
  2. 客户端接收到之后,下次再调用该资源时会在请求头中添加 is-None-Match(就是服务器返回的 etag 字段)
  3. 服务端拿到这个时间并再次读取该资源的哈希值,让他们两个做一个比对来决定是读取缓存还是返回新的资源
缺点
  1. ETag 需要计算文件指纹这样意味着,服务端需要更多的计算开销。。如果文件尺寸大,数量多,并且计算频繁,那么 ETag 的计算就会影响服务器的性能。显然,ETag 在这样的场景下就不是很适合。
  1. ETag 有强验证和弱验证,所谓将强验证,ETag 生成的哈希码深入到每个字节。哪怕文件中只有一个字节改变了,也会生成不同的哈希值,它可以保证文件内容绝对的不变。但是,强验证非常消耗计算量。ETag 还有一个弱验证,弱验证是提取文件的部分属性来生成哈希值。因为不必精确到每个字节,所以他的整体速度会比强验证快,但是准确率不高。会降低协商缓存的有效性。

总结

值得注意的一点是,不同于 cache-control 是 expires 的完全替代方案(说人话:能用 cache-control 就不要用 expiress)。ETag 并不是 last-modified 的完全替代方案。而是 last-modified 的补充方案(说人话:项目中到底是用 ETag 还是 last-modified 完全取决于业务场景,这两个没有谁更好谁更坏)。

  • http 缓存可以减少宽带流量,加快响应速度。
  • 关于强缓存,cache-control 是 Expires 的完全替代方案,在可以使用 cache-control 的情况下不要使用 expires
  • 关于协商缓存,etag 并不是 last-modified 的完全替代方案,而是补充方案,具体用哪一个,取决于业务场景。
  • 有些缓存是从磁盘读取,有些缓存是从内存读取,有什么区别?答:从内存读取的缓存更快。
  • 所有带 304 的资源都是协商缓存,所有标注(从内存中读取/从磁盘中读取)的资源都是强缓存。

参考文章

你可能感兴趣的:(技术点,缓存,前端,服务器)