• 通俗易懂,一文学会前端缓存


    什么是web缓存

    主要分为:浏览器缓存、http缓存

    其中http缓存是web缓存的核心,最难懂也是最重要的一部分。

    浏览器缓存,如localStorage,sessionStorage,cookie等等。这些功能主要用于缓存一些必要的数据,比如用户信息,需要携带给后端的参数。(一般只能保存5M左右的数据,多了不行)

    所以接下来,我们的重点将围绕http缓存展开。

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

    可以解决的问题:

    • 减少不必要的网络传输,节约宽带(省钱小窍门)
    • 更快的页面加载速度(加速)
    • 减少服务器负载,避免服务器过载(减载)

    存在的缺点:

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

    http缓存又分为两种:

    • 强制缓存
    • 协商缓存

    强制缓存

    简称强缓存。如果浏览器判断请求的目标资源有效命中强缓存,如果直接命中,则可以直接从内存中读取目标资源,无需于服务器做任何通讯。

    基于Expires字段实现的强缓存

    Expires的作用是,设定一个强缓存时间,在此时间范围内,则从内存或磁盘中读取缓存返回。

    但是Expires已经被废弃了!对于强缓存来说,Expires已经不是实现强缓存的首选。

    因为Expires判断强缓存是否过期的机制是:获取本地时间戳,并对比先前拿到资源文件的Expires字段做比较,这里就存在一个巨大的bug:如果我的本地时间不准怎么办?

    对,Expires太过于依赖本地时间,如果本地时间与服务器时间不同步,就会出现资源无法被缓存或者资源永远被缓存的情况。所以,Expires字段几乎不被使用了。在现在的项目中,我们并不推荐使用Expires,强缓存功能通常使用cache-control字段代替Expires。

    没想到吧,说了半天,结果是给废弃的东西。

    基于Cache-control实现的强缓存

    Cache-control完美解决了Expires本地时间的bug。

    Cache-control的使用方法很简单,只要在资源的相应头上写上需要缓存多久就好了:

    res.writeHead(200,{
        'Cache-Control':'max-age=10'
    });
    
    • 1
    • 2
    • 3

    从资源第一次返回的时候开始,往后的10s中内如果资源在此请求,则从缓存中读取。

    Cache-Control:max-age=NN就是需要缓存的秒数。从第一次请求资源的时候开始,往后N秒内,资源若再次请求,则直接从磁盘(或内存中读取),不与服务器做任何交互。

    Cache-control中max-age后面的值上一个滑动时间,从服务器第一次返回该资源开始倒计时,所以不需要对比客户端与服务器之间的时间,也就解决了Expires的巨大bug。

    Cache-control的属性:

    • max-age:客户端资源被缓存多久
    • s-maxage:代理服务器缓存的时长
    • no-cache:强制进行协商缓存(直接跳过强缓存的校验)
    • no-store:禁止任何资源缓存
    • public:资源可被浏览器缓存,也可被代理服务器缓存
    • private:资源只能被浏览器缓存

    ⚠️注意:public与private互斥,默认为private;max-age与s-maxage不互斥,可以一起使用

    协商缓存

    协商缓存的内容会有点绕,我们仔细看看。

    基于last-modified的协商缓存

    过程:

    1. 首先需要在服务器端读出文件修改时间
    2. 将读出来的修改时间赋值给响应头的last-modified字段
    3. 最后设置Cache-control:no-cache

    这个过程缺一不可。

    还没结束,到这里还不能实现协商缓存。

    当客户端读取到last-modified的时候,会在下次请求标头中携带一个字段If-Modified-Since,而这个请求头中的If-Modified-Since就是服务器第一次修改时候给他的时间。那么之后每次对该资源的请求,都会带上If-Modified-Since这个字段,而服务端需要拿到这个时间并再次读取资源的修改时间,让两者做对比决定上读取缓存还是返回新的资源。

    使用以上方法的协商缓存存在两个非常明显的bug,这两个bug都是基于文件是通过对比修改时间来判断是否内容更改的。

    1. 因为是根据文件修改时间来判断,所以,在文件内容本身不修改的情况下,依然有可能更新文件修改时间,导致缓存失效了。
    2. 当文件在极短时间内完成修改(几百毫秒),而文件修改时间的最小单位是秒,所以这个时候文件修改时间不会改变,导致依然不会返回新的文件

    为了解决这两个bug,从http1.1开始新增了一个头信息,ETag,顶住!!马上就顺通了!

    基于ETag的协商缓存

    ETag就是将原先协商缓存的比较时间戳的形式 -> 比较文件指纹

    文件指纹:根据文件内容计算出的唯一哈希值,一旦内容改变哈市值随之改变,指纹也改变。

    过程:

    1. 第一次请求资源的时候,服务端读取文件并计算出文件指纹,将文件指纹放在响应头的ETag字段跟资源一起返给客户端
    2. 第二次请求资源时,客户端自动从缓存中读取上一次服务器返回的ETag。并赋给请求头的if-None-Match字段,让上一次的文件指纹跟随请求一起回到服务端
    3. 服务端拿到请求头的if-None-Match字段值,并再次读取目标资源生成文件指纹,两个指纹做对比,如果完全一致,则直接返回304状态码和一个空的相应体;如果不吻合,则说明文件被修改,那么将新的文件指纹放在响应头的ETag中返回给客户端

    ETag也有缺点

    • ETag需要计算文件指纹,服务端需要更多的计算开销,如果文件尺寸大,数量多,计算频繁的话,ETag计算就会影响服务端性能,在这种场景下就不太适用
    • ETag又强验证和弱验证,所谓强验证,ETag生成的哈希码深入到每个字节。哪怕文件中只有一个字节改变了,也会生成不同的哈希值,所以强验证非常消耗计算量;弱验证是提取文件的部分属性来生成哈希值,因为不必精确到每个字节,所以整体速度较快,但准确率不高。

    总结一下

    • http缓存可以减少带宽流量,加快响应速度
    • 关于强缓存,cache-control是Expires的完全替代方案,在可以使用cache-control的情况下不要使用expires
    • 关于协商缓存,etag并不是last-modified的完全替代方案,而是补充方案,具体用哪一个,取决于业务场景
    • 有些缓存是从磁盘读取,有些缓存是从内容中读取,有什么区别?从内存中读取的缓存更快
    • 所有带304的资源都是协商缓存,所有标注从内存/磁盘中读取的资源都是强缓存
  • 相关阅读:
    Windows10系统安装VMWare
    Mac上使用Royal TSX快速连接到OCI主机
    Python + Django4 搭建个人博客(五): 创建并连接数据库
    sql 10
    round函数四舍五入结果不对
    消息队列十连问
    RAFT 算法简述
    Docker系列 自建代码托管和版本控制平台Gogs
    反序列化漏洞
    华为OD机试真题-数组拼接-2023年OD统一考试(B卷)
  • 原文地址:https://blog.csdn.net/weixin_42224055/article/details/126605440