前端缓存

212 阅读4分钟

前端缓存

  • http缓存
    • 强缓存
    • 协商缓存
  • 浏览器缓存
    • cookie
    • localStroage
    • sessionStroage
    • serviceWorker
      • 离线储存
      • 页面间的通信

前端缓存用来干什么?

前端缓存可以帮我们减少网络请求次数,减少流量消耗。

http缓存

  • chrome disable cache 取消勾选后查看缓存情况
  1. 强缓存
  • 浏览器自己会做一些缓存策略

  • 强缓存优于协商缓存

  • 强缓存中 Cache-control 优先级高于 Expries

  • 第一次网络请求后,第二次请求头部有 Cache-control 字段且未过期,则从缓存取数据

  • Cache-control (相对值) / Expries(绝对值)

    • Expries是http1.0的标准

          let nowTime = new Date();
          nowTime.setTime(new Date().getTime() + 3600*1000);
          ctx.set("Expires",nowTime.toUTCString());
      
    • 到了HTTP/1.1,Expire已经被Cache-Control替代

      ctx.set("Cache-control","max-age=3600")  //设置缓存时间  3600s
      

      (1)public:所有内容都将被缓存(客户端和代理服务器都可缓存)

      (2)private:所有内容只有客户端可以缓存,Cache-Control的默认取值

      (3)no-cache:客户端缓存内容,但是是否使用缓存则需要经过协商缓存来验证决定

      (4)no-store:所有内容都不会被缓存,即不使用强制缓存,也不使用协商缓存

      (5)max-age=xxx :缓存内容将在xxx秒后失效

    • 根据文件类型自动分配存储位置

    • from memory cache代表使用内存中的缓存,速度快

    • from disk cache则代表使用的是硬盘中的缓存

    • 浏览器读取缓存的顺序为memory –> disk

    • 网站终止,内存会清空,而硬盘中的数据不主动清除则会一直存在

  1. 协商缓存
  • Last-Modify / if-Modify-Since
  • ETag / if-None-Macth
  • 协商缓存中 ETag / If-None-Match 优先级高于 Last-Modified / If-Modified-Since
  • 是否读缓存,由服务器端决定
  • 协商缓存的标识也是在响应报文的HTTP头中和请求结果一起返回给浏览器的,控制协商缓存的字段分别有:Last-Modified / If-Modified-Since和Etag / If-None-Match,其中Etag / If-None-Match的优先级比Last-Modified / If-Modified-Since高。
  • 过程:Last-Modify 记录文件/接口修改时间 -> 客户端收到 response header 的 Last-Modify, 下次请求时,request header 带if-Modify-Since -> 无论如何都会发送请求,返回200或者304,服务器拿到时间后与服务器上的修改时间对比,如一致则返回304状态,此时比直接200的带宽损耗小。
  • Etag / If-None-Match 与 Last-Modified / If-Modified-Since 的区别在于:
    • Etag / If-None-Match : 服务端提取哈希值或者md5的摘要,更精确且时间更短
    • Last-Modified / If-Modified-Since : 文件的修改时间,内容改了而时间未被修改,可能监控不到。对比服务器的时间间隔为1s

浏览器缓存

本地存储

  • cookie

    • 主要功能

      • 客户端与服务端之间通信

        • 每次通信间会携带 (如果非必要会消耗流量)
      • 本地存储

        • 大小小于4k
      • 设置cookie

        let d = new Date();
        d.setTime(d.getTime()+3600*1000);
        document.cookie = "uss=lisi; expires="+d.toGMTString();
      
      • 读取cookie
      document.cookie
      
      • 前后端都可以设置
      • 可以用来标志网络请求的状态
  • webstroage

    • localStroage(大小不大于5M,【每个浏览器大小不一定】没有过期时间)

      • 本地储存

        • 存储

          localStroage.setItem(key,value);
          
        • 读取

          localStorage.getItem('key')
          
      • 利用localStroage做本地缓存

        if(localStorage.getItem("test")){
            let res = eval(localStorage.getItem("test"));
        }else{
            let xhr = new XMLHttpRequest();
            xhr.open("GET","./test.js",true);
            xhr.onload = function(){
                eval(xhr.responseText);
                localStorage.setItem("test",xhr.responseText);
            }
            xhr.send();
        }
        
    • sessionStroage

      • 会话存储,提高用户体验,关闭浏览器时自动清除(整个浏览器退出)
      • 接口同上
      • 用途:表单填写时,如中途刷新页面,已填写的数据可以保存下来
  • service worker

    • 拦截客户端请求,离线缓存;

    • 不占用js主线程

    • 针对域名存储,可存储所有东西

    • 离线缓存

      • 注册service worker

        if ('serviceWorker' in window.navigator) {
            navigator.serviceWorker.register('./sw.js', { scope: './' })
              .then(function (reg) {
                console.log('success', reg);
              })
              .catch(function (err) {
                console.log('fail', err);
              });
            }  
        
      • 监听install事件将文件缓存到cache Stroage

        this.addEventListener("install",function(event){
          console.log('Service Worker install');
          event.waitUntil(
            caches.open("mytest").then(cache=>{
                return cache.addAll(["./a.js","./style.css","./index.html","index.js"]);
            })
          )
        })
        
      • 拦截网络请求

        this.addEventListener("fetch",event=>{
          event.respondWith(
            caches.match(event.request).then( function (res) {
                return res
            })
          )
        })
        
    • 页面间通信

      • 注册service worker

        navigator.serviceWorker.register("./messagesw.js",{scope:"./"}).then(res=>{
                    console.log("ServiceWorker work success");
                }).catch(err=>{
                    console.log(err);
                })
        
      • 发送消息到serviceWorker

         // 发送消息到servicework
                document.querySelector(".btn").onclick = function(){
                    let value = document.querySelector(".myinp").value;
                    // console.log(value);
                    navigator.serviceWorker.controller.postMessage(value);
                }
        
      • serviceWorker接收信息

        this.addEventListener("message",function(event){
            // console.log(event);
            var promise = this.clients.matchAll().then ( function (clientList) {
                var senderID = event.source ? event.source.id : 'unknown'
                clientList.forEach( function (client) {
                  if (client.id == senderID) {
                    return
                  } else {
                    client.postMessage({
                      client: senderID,
                      message: event.data
                    })
                  }
                })
              })
              event.waitUntil(promise)
        })
        
      • 前端js监听消息

         navigator.serviceWorker.addEventListener("message",e=>{
                    // console.log(e.data);
                    let div = document.createElement("div");
                    div.innerHTML = `${e.data.message}`;
                    document.querySelector("body").appendChild(div);
                })