缓存

作者: 暖暖1500 | 来源:发表于2021-10-22 16:17 被阅读0次

    这篇文章对前端缓存做一个简单的初步的概述。

    前端缓存就是把一部分的数据保存在客户端,减少对服务器的请求,降低服务器的压力,提升网页的加载速度,优化用户体验,本文主要围绕下图对缓存进行介绍,如有理解错误的地方,欢迎指正


    缓存.png
    一 http缓存
    缓存过程的解析

    浏览器与服务器通信的方式为应答模式,即:浏览器发起HTTP请求 – 服务器响应该请求。那么浏览器第一次向服务器发起该请求后拿到请求结果,会根据响应报文中HTTP头的缓存标识,决定是否缓存结果,是则将请求结果和缓存标识存入浏览器缓存中,简单的过程如下图:


    浏览器请求服务器.png

    由上图我们可以知道,浏览器每次发起请求,都会在浏览器缓存中查找该请求的结果及缓存标识,然后浏览器每次拿到返回的请求结果都会将该结果和缓存标识存入浏览器缓存。
    这里我们根据是否需要向服务器重新发起HTTP请求将缓存过程分为两个部分,分别为强制缓存和协商缓存。

    1. 强制缓存
      强制缓存就是向浏览器缓存查找该请求结果,并根据该结果的缓存规则来决定是否使用该缓存结果的过程,强制缓存的情况主要分为3种,如下:
      1.1 不存在该缓存结果和缓存标识,强制缓存失效,则直接向服务器发起请求(跟第一次发起请求一致),如下图:


      强制缓存不存在或者失效.png

      1.2 存在该缓存结果和缓存标识,但是结果已经失效,强制缓存失效,则使用协商缓存(下文中介绍),如下图:


      强制缓存失效.png
      1.3 存在该缓存结果和缓存标识,且该结果没有还没有失效,强制缓存生效,直接返回该结果,如下图:
      强制缓存生效.png
      1.4 强制缓存的规则
      当浏览器向服务器发送请求的时候,服务器会将缓存规则放入HTTP响应的报文的HTTP头中和请求结果一起返回给浏览器,控制强制缓存的字段分别是Expires和Cache-Control,其中Cache-Control的优先级比Expires高。

      1.4.1 Expires
      Expires是HTTP/1.0控制http缓存的字段,其值为服务器返回该请求的结果缓存的到期时间,即再次发送请求时,如果客户端的时间小于Expires的值时,直接使用缓存结果。

      但是现在浏览器的默认使用的是HTTP/1.1,那么在HTTP/1.1中http缓存的控制标识Expires已经被Cache-Control替代,原因在于Expires控制缓存的原理是使用客户端的时间与服务端返回的时间做对比,如果客户端与服务端的时间由于某些原因(时区不同;客户端和服务端有一方的时间不准确)发生误差,那么强制缓存直接失效,那么强制缓存存在的意义就毫无意义。

      1.4.2 Cache-Control
      在HTTP/1.1中,Cache-Control是最重要的规则,主要用于控制http缓存,主要取值为:
      (1)public:所有内容都将被缓存(客户端和代理服务器都可缓存)
      (2)private:所有内容只有客户端可以缓存,Cache-Control的默认取值
      (3)no-cache:客户端缓存内容,但是是否使用缓存则需要经过协商缓存来验证决定
      (4)no-store:所有内容都不会被缓存,即不使用强制缓存,也不使用协商缓存
      (5)max-age=xxx (xxx is numeric):缓存内容将在xxx秒后失效
      接下来我们看一个请求的例子


      请求截图.png

      由上面的例子我们可以知道:
      (1)HTTP响应报文中expires的时间值,是一个绝对值
      (2)HTTP响应报文中Cache-Control为max-age=600,是相对值
      由于Cache-Control的优先级比expires,那么直接根据Cache-Control的值进行缓存,意思就是说在600秒内再次发起该请求,则会直接使用缓存结果,强制缓存生效。
      注:在无法确定客户端的时间是否与服务端的时间同步的情况下,Cache-Control相比于expires是更好的选择,所以同时存在时,只有Cache-Control生效。

    2.协商缓存
    协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程,主要有以下两种情况:
    2.1 协商缓存生效,返回304,如下


    协商缓存生效.png

    2.2 协商缓存失败,返回200和请求结果,如下:


    协商缓存失败.png

    同样,协商缓存的标识也是在响应报文的HTTP头中和请求结果一起返回给浏览器的,控制协商缓存的字段分别有:Last-Modified / If-Modified-Since和Etag / If-None-Match,其中Etag / If-None-Match的优先级比Last-Modified / If-Modified-Since高。
    2.2.1 Last-Modified / If-Modified-Since
    (1)Last-Modified是服务器响应请求时,返回该资源文件在服务器最后被修改的时间,如下:


    最后修改时间.png

    (2) If-Modified-Since则是客户端再次发起该请求时,携带上次请求返回的Last-Modified值,通过此字段值告诉服务器该资源上次请求返回的最后被修改时间。服务器收到该请求,发现请求头含有If-Modified-Since字段,则会根据If-Modified-Since的字段值与该资源在服务器的最后被修改时间做对比,若服务器的资源最后被修改时间大于If-Modified-Since的字段值,则重新返回资源,状态码为200;否则则返回304,代表资源无更新,可继续使用缓存文件,如下。


    客户端再次发起请求.png
    2.2.2 Etag / If-None-Match
    (1)Etag是服务器响应请求时,返回当前资源文件的一个唯一标识(由服务器生成),如下:
    资源文件的唯一标识Etag.png

    (2)If-None-Match是客户端再次发起该请求时,携带上次请求返回的唯一标识Etag值,通过此字段值告诉服务器该资源上次请求返回的唯一标识值。服务器收到该请求后,发现该请求头中含有If-None-Match,则会根据If-None-Match的字段值与该资源在服务器的Etag值做对比,一致则返回304,代表资源无更新,继续使用缓存文件;不一致则重新返回资源文件,状态码为200,如下:


    if-none-match.png
    总结

    强制缓存优先于协商缓存进行,若强制缓存(Expires和Cache-Control)生效则直接使用缓存,若不生效则进行协商缓存(Last-Modified / If-Modified-Since和Etag / If-None-Match),协商缓存由服务器决定是否使用缓存,若协商缓存失效,那么代表该请求的缓存失效,重新获取请求结果,再存入浏览器缓存中;生效则返回304,继续使用缓存,主要过程如下:


    浏览器缓存过程图.png
    二 缓存位置

    介绍了强制缓存和协商缓存,来扩展下浏览器的缓存存放在哪里?
    打开控制台看文件的请求发现有了size的字段表示了缓存存放的位置,分别为from memory cache 和 from disk cache。

    1. memory cache
      代表使用内存中的缓存,有两个特点,分别是快速读取和时效性,1> 快速读取:内存缓存会将编译解析后的文件,直接存入该进程的内存中,占据该进程一定的内存资源,以方便下次运行使用时的快速读取。2> 时效性:一旦该进程关闭,则该进程的内存则会清空。

    2. disk cache
      代表了使用的是硬盘中的缓存, 硬盘缓存则是直接将缓存写入硬盘文件中,读取缓存需要对该缓存存放的硬盘文件进行I/O操作,然后重新解析该缓存内容,读取复杂,速度比内存缓存慢。
      浏览器读取缓存的顺序为memory –> disk,一般对一个网页的访问过程是这样的:访问某一个网页返回200 –> 关闭标签页 –> 重新打开网页 –>200(from disk cache) –> 刷新 –> 200(from memory cache)。

    3. Service Worker
      是运行在浏览器背后的独立线程,一般可以用来实现缓存功能。使用 Service Worker的话,传输协议必须为 HTTPS。因为 Service Worker 中涉及到请求拦截,所以必须使用 HTTPS 协议来保障安全。Service Worker 的缓存与浏览器其他内建的缓存机制不同,它可以让我们自由控制缓存哪些文件、如何匹配缓存、如何读取缓存,并且缓存是持续性的。

      Service Worker 实现缓存功能一般分为三个步骤:首先需要先注册 Service Worker,然后监听到 install 事件以后就可以缓存需要的文件,那么在下次用户访问的时候就可以通过拦截请求的方式查询是否存在缓存,存在缓存的话就可以直接读取缓存文件,否则就去请求数据。.

      当 Service Worker 没有命中缓存的时候,我们需要去调用 fetch 函数获取数据。也就是说,如果我们没有在 Service Worker 命中缓存的话,会根据缓存查找优先级去查找数据。但是不管我们是从 Memory Cache 中还是从网络请求中获取的数据,浏览器都会显示我们是从 Service Worker 中获取的内容。

    4. Push Cache(推送缓存)
      Push Cache(推送缓存)是 HTTP/2 中的内容,当以上三种缓存都没有命中时,它才会被使用。它只在会话(Session)中存在,一旦会话结束就被释放,并且缓存时间也很短暂,在Chrome浏览器中只有5分钟左右,同时它也并非严格执行HTTP头中的缓存指令。
      所有的资源都能被推送,并且能够被缓存,但是 Edge 和 Safari 浏览器支持相对比较差 - 可以推送 no-cache 和 no-store 的资源 - 一旦连接被关闭,Push Cache 就被释放 - 多个页面可以使用同一个HTTP/2的连接,也就可以使用同一个Push Cache。这主要还是依赖浏览器的实现而定,出于对性能的考虑,有的浏览器会对相同域名但不同的tab标签使用同一个HTTP连接。 - Push Cache 中的缓存只能被使用一次 - 浏览器可以拒绝接受已经存在的资源推送 - 你可以给其他域名推送资源

    三 本地存储
    1. LocalStroage
      是HTML5新加入的特性,在浏览器中以key/value 对的存储数据,保存的数据没有过期时间,永久性存储,直到手动去删除。一般浏览器支持5M大小的存储空间,这个在不同的浏览器中localStorage会有所不同。
      1.1 浏览器的支持情况:


      localStorage浏览器支持情况.png

      1.2 目前所有的浏览器中都会把localStorage的值类型限定为string类型,这个在对我们日常比较常见的JSON对象类型需要一些转换,localStorage本质是对字符串的读取,如果存储的内容过多话就会消耗内存空间,导致页面变卡。
      1.3 localStorage在浏览器的隐私模式下面是不可读取的,而且不能被爬虫抓取到
      1.4 使用语法(直接访问window.localStorage返回一个JS的存储对象)

       let localStorage = window.localStorage;
       // 保存
       localStorage.setItem("key", "value");
        // 使用
       let value = localStorage.getItem("key");
        // 删除
       localStorage.removeItem("key");
      
    2. SessionStroage
      是HTML5新加入的特性,在浏览器中以key/value 对的存储数据,一般浏览器支持5M大小的存储空间,这个在不同的浏览器中sessionStroage会有所不同。sessionStorage存储的数据只有在同一个会话中的页面才能访问并且当会话结束后数据也随之销毁。因此sessionStorage不是一种持久化的本地存储,仅仅是会话级别的存储。个人的理解是你在打开一个页面时记录sessionStorage,当你把页面或者浏览器关闭时session中的数据即销毁。
      1.1 浏览器的支持情况:


      SessionStroage浏览器支持.png

      1.2 使用语法(直接访问window.sessionStroage返回一个JS的存储对象)

        let sessionStroage = window.sessionStroage;
        // 保存
        sessionStroage.setItem("key", "value");
         // 使用
        let value = sessionStroage.getItem("key");
         // 删除
        sessionStroage.removeItem("key");
         // 删除所有的数据
        sessionStorage.clear();
      
    3. Cookie
      cookie大部分浏览器的大小限制为4K左右,根据浏览器的不同,有一定的差异,还有数量的限制,浏览器允许每个域名包含的cookie数也不尽相同。如果你想支持大多数浏览器,那么每个域不要超过50个cookie,每个域不要超过4093个字节。所有cookie的总大小< = 4093字节)
      3.1 cookie限制


      cookie限制.png

      3.2 当很多的cookie被设置,浏览器如何去响应。
      3.2.1 除Safari(可以设置全部cookie,不管数量多少),有两个方法:
      3.2.2 最少最近使用(leastrecentlyused(LRU))的方法:当Cookie已达到限额,自动踢除最老的Cookie,以使给最新的Cookie一些空间。InternetExplorer和Opera使用此方法。
      3.2.3 Firefox很独特:虽然最后的设置的Cookie始终保留,但似乎随机决定哪些cookie被保留。似乎没有任何计划(建议:在Firefox中不要超过Cookie限制)
      3.3 使用语法

        // 设置cookie的函数
         function setCookie(cname,cvalue,exdays) {
               var d = new Date();
               d.setTime(d.getTime()+(exdays*24*60*60*1000));
               var expires = "expires="+d.toGMTString();
               document.cookie = cname + "=" + cvalue + "; " + expires;
           }
         // 获取 cookie 值的函数
         function getCookie(cname) {
             var name = cname + "=";
             var ca = document.cookie.split(';');
             for(var i=0; i<ca.length; i++) {
               var c = ca[i].trim();
               if (c.indexOf(name)==0) return c.substring(name.length,c.length);
             }
             return "";
         }
      

      3.4 深入理解cookie与常见应用
      客户端向服务器发起请求,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。客户端浏览器会把Cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器检查该Cookie,以此来辨认用户状态。
      3.4.1 cookie机制
      当用户第一次访问并登陆一个网站的时候,cookie的设置以及发送会经历以下4个步骤:客户端发送一个请求到服务器 --》 服务器发送一个HttpResponse响应到客户端,其中包含Set-Cookie的头部 --》 客户端保存cookie,之后向服务器发送请求时,HttpRequest请求中会包含一个Cookie的头部 --》服务器返回响应数据,具体如下图所示:


      cookie.png

      3.4.2 cookie的属性项

      属性项 属性介绍
      NAME=VALUE 键值对,可以设置要保存的 Key/Value,注意这里的 NAME 不能和其他属性项的名字一样
      Expires 过期时间,在设置的某个时间点后该 Cookie 就会失效
      Domain 生成该 Cookie 的域名,如 domain="www.baidu.com"
      Path 该 Cookie 是在当前的哪个路径下生成的,如 path=/wp-admin/,path属性决定允许访问Cookie的路径。比如,设置为"/"表示允许所有路径都可以使用Cookie
      Secure 如果设置了这个属性,那么只会在 SSH 连接时才会回传该 Cookie
      Expires 介绍

      该属性用来设置Cookie的有效期。Expires有3种值,分别为正数,负数, session和0。1> 如果Expires属性为正数,则表示该Cookie会在Expires秒之后自动失效。浏览器会将Expires为正数的Cookie持久化,即写到对应的Cookie文件中(每个浏览器存储的位置不一致)。无论客户关闭了浏览器还是电脑,只要还在Expires秒之前,登录网站时该Cookie仍然有效。2> 当Expires属性为负数,则表示该Cookie只是一个临时Cookie,不会被持久化,仅在本浏览器窗口或者本窗口打开的子窗口中生效,关闭浏览器后该Cookie失效。3>Expires属性为Seesion同样的时间失效,属于一个默认值,将会在你关闭浏览器之后失效, 4> 当Expires为0时,表示立即删除Cookie。

    4. IndexDB(数据库缓存)
      IndexDB 就是浏览器提供的本地数据库,它可以被网页脚本创建和操作。IndexedDB 允许储存大量数据,提供查找接口,还能建立索引。这些都是 LocalStorage 所不具备的。就数据库类型而言,IndexedDB 不属于关系型数据库(不支持 SQL 查询语句),更接近 NoSQL 数据库。
      4.1IndexDB的特点
      4.1.1 键值对储存。 IndexedDB 内部采用对象仓库(object store)存放数据。所有类型的数据都可以直接存入,包括 JavaScript 对象。对象仓库中,数据以"键值对"的形式保存,每一个数据记录都有对应的主键,主键是独一无二的,不能有重复,否则会抛出一个错误。
      4.1.2 异步。 IndexedDB 操作时不会锁死浏览器,用户依然可以进行其他操作,这与 LocalStorage 形成对比,后者的操作是同步的。异步设计是为了防止大量数据的读写,拖慢网页的表现。
      4.1.3 支持事务。 IndexedDB 支持事务(transaction),这意味着一系列操作步骤之中,只要有一步失败,整个事务就都取消,数据库回滚到事务发生之前的状态,不存在只改写一部分数据的情况。
      4.1.4 同源限制 IndexedDB 受到同源限制,每一个数据库对应创建它的域名。网页只能访问自身域名下的数据库,而不能访问跨域的数据库。
      4.1.5 储存空间大 IndexedDB 的储存空间比 LocalStorage 大得多,一般来说不少于 250MB,甚至没有上限。
      4.1.6 支持二进制储存。 IndexedDB 不仅可以储存字符串,还可以储存二进制数据(ArrayBuffer 对象和 Blob 对象)。
      4.2 基本概念
      4.2.1 数据库:IDBDatabase 对象
      数据库是一系列相关数据的容器。每个域名(严格的说,是协议 + 域名 + 端口)都可以新建任意多个数据库。
      IndexedDB 数据库有版本的概念。同一个时刻,只能有一个版本的数据库存在。如果要修改数据库结构(新增或删除表、索引或者主键),只能通过升级数据库版本完成。
      4.2.2 对象仓库:IDBObjectStore 对象
      每个数据库包含若干个对象仓库(object store)。它类似于关系型数据库的表格。对象仓库保存的是数据记录。每条记录类似于关系型数据库的行,但是只有主键和数据体两部分。主键用来建立默认的索引,必须是不同的,否则会报错。主键可以是数据记录里面的一个属性,也可以指定为一个递增的整数编号。
      数据体可以是任意数据类型,不限于对象。
      4.2.3 索引: IDBIndex 对象
      为了加速数据的检索,可以在对象仓库里面,为不同的属性建立索引。
      4.2.4 事务: IDBTransaction 对象
      数据记录的读写和删改,都要通过事务完成。事务对象提供error、abort和complete三个事件,用来监听操作结果
      4.2.5 操作请求:IDBRequest 对象
      4.2.6 指针: IDBCursor 对象
      4.2.7 主键集合:IDBKeyRange 对象
      具体使用见 https://wangdoc.com/javascript/bom/indexeddb.html#indexeddb-%E5%AF%B9%E8%B1%A1

    5. AppCache(应用层缓存)
      HTML5引进的新技术,意味着web应用可进行缓存,并在没有网络的情况下使用。
      5.1 Application Cache带来的三个优势
      5.1.1 离线浏览
      5.1.2 提升页面载入速度
      5.1.3 降低服务器压力
      5.2 使用
      使用appcache的话,需要在index.html下,也就是项目目录下,新建一个类似于cache.manifest的manifest文件并在index.html下的html标签内新增manifest属性, 就是在html中引入一个minifest的文件,该文件声明了哪些文件应该被缓存,哪些文件不会被缓存。

         <!DOCTYPE HTML>
          <html manifest = "cache.manifest">
            ...
          </html>
    

    minifest 文件的格式如下:

        CACHE MANIFEST
        #v0.11 首次下载后进行缓存的,一般会写版本号
        CACHE:
        #需要被缓存的文件
        js/app.js
        css/style.css
    
        NETWORK:
        #不需要被缓存的文件
        resourse/logo.png
    
        FALLBACK:
        #表示如果访问第一个资源失败,那么就使用第二个资源来替换他,比如上面 
        #这个文件表示的就是如果访问根目录下任何一个资源失败了,那么就去访问 
        offline.html。
        offline.html
    

    相关文章

      网友评论

          本文标题:缓存

          本文链接:https://www.haomeiwen.com/subject/ctoyzftx.html