前端持久化之本地存储

0 阅读11分钟

一、特性一览

特性cookielocalStoragesessionStorageindexDB
数据生命周期一般由服务器生成,可以设置过期时间;前端采用和js-cookie等组件也可以生成除非被清理,否则一直存在;浏览器关闭还会保存在本地,但是不支持跨浏览器页面关闭就清理刷新依然存在,不支持跨页面交互除非被清理,否则一直存在
数据存储大小4K5M5M不限制大小
与服务端通信每次都会携带在请求的header 中,对于请求性能有影响;同时由于请求中都带有,所以也容易出现安全问题不参与不参与不参与
特点字符串键值对在本地存储数据字符串键值对在本地存储数据字符串键值对在本地存储数据IndexedDB 是一个非关系型数据库(不支持通过 SQL 语句操作)。可以存储大量数据,提供接口来查询,还可以建立索引,这些都是其他存储方案无法提供的能力。

tips:

  1. 浏览器控制台的application中的storage可以看到对应的四种存储
  2. HTML5本地存储只能存字符串,任何格式存储的时候都会被自动转为字符串,所以读取的时候,需要自己进行类型的转换
  3. 要注意在前端操作的存储和后端数据库存储一样都是异步的,即取的时候有可能会出现还没存好的可能

二、深入探究四种方案

1). cookie

  1. cookie 简介
    现在主流的服务架构都是B/S架构,浏览器和服务器的通信一般就是通过http请求,而http又是无状态的,起初只是为了解决这个无状态的问题,Cookie 是服务器发送到用户浏览器并保存在本地的一小块数据。它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。
  2. cookie 操作
    2.1 双端操作
    • 服务器端,可以通过 HTTP 响应头 Set-Cookie 来生成和设置 Cookie。

    • 在客户端,可以通过 JavaScript 的 document.cookie 来生成和设置 Cookie。【响应头中可以通过设置httpOnly字段控制是否运行客户端操作cookie】 2.2 . 代码实现 /*

      • 服务器生成Cookie

      • */ @RequestMapping(path = "/cookie/set", method = RequestMethod.GET) @ResponseBody public String setCookie(HttpServletResponse response) { Cookie cookie = new Cookie("code", CommunityUtil.generateUUID()); // 存在验证信息 cookie.setMaxAge(60 * 10); // 设置过期时间 cookie.setPath("/community/alpha"); // 设置生效路径 response.addCookie(cookie); // 将Cookie放入到响应报文中

        return "set cookie"; }

      /*

      • 浏览器获取Cookie

      • */ @RequestMapping(path = "/cookie/get", method = RequestMethod.GET) @ResponseBody public String getCookie(@CookieValue("code") String code) { System.out.println(code);

        return "get cookie"; }

  3. 相关字段以及注意事项
    • name:名称是Cookie的唯一标识符,服务器和客户端通过名称来访问和操作Cookie
    • value:值是存储在Cookie中的实际数据,可以是字符串、数字或其他格式【存储的时候都会转化成字符串,非字符串数据最好手动解析】
    • Expires:设置Cookie的过期时间。如果未设置,Cookie将在浏览器关闭时删除,过期时间是一个GMT格式的日期,指定Cookie在什么时间之后失效
    • Max-Age:设置Cookie的最大存活时间(以秒为单位)。与Expires类似,但优先级更高。表示Cookie将在xxx秒后过期。
    • Domain:指定Cookie可用的域名。如果不设置,默认为当前域名。设置后,Cookie可以在example.com及其子域名(如sub.example.com)下使用。
    • Path:指定Cookie可用的路径。如果不设置,默认为当前路径。设置后,Cookie只能在指定的路径及其子路径下使用。例如,Path=/app表示Cookie只能在/app及其子路径(如/app/dashboard)下访问。
    • Secure:设置此标志后,Cookie只能通过HTTPS协议传输。用于保护Cookie在传输过程中不被窃取,建议在涉及敏感信息时使用。
    • HttpOnly: 设置此标志后,Cookie无法通过JavaScript访问,只能通过HTTP请求传输。用于防止XSS(跨站脚本)攻击,保护Cookie不被恶意脚本读取。
    • SameSite:控制Cookie在跨站请求中的发送行为。可选值包括Strict、Lax和None
      • Strict: 仅在同站请求中发送Cookie(例如,从example.com到example.com)。
      • Lax: 在导航到目标站点的GET请求中发送Cookie(例如,从example.com点击链接到example.com)。
      • None: 在所有请求中发送Cookie(需要同时设置Secure标志)。

image.png

2). localStorage

  1. 简介
    本地存储(Local Storage)是指在用户浏览器中存储数据的方式,它允许 Web 应用程序将少量数据保存在用户设备上,方便页面之间、甚至关闭浏览器后的数据持久化
    • 硬盘:数据通常存储在用户的计算机硬盘上,具体路径因操作系统和浏览器而异。
    • 内存:当浏览器启动时,localStorage 数据会被加载到内存中,以便快速访问。关闭浏览器后,数据仍保留在硬盘上。
  2. 操作方法和具体使用
  • CRUD操作 // 增加了一个 localStorage ‘myCat’ 数据项 localStorage.setItem('myCat', 'Tom');

     // 读取 localStorage ‘myCat’ 数据项
     let cat = localStorage.getItem('myCat');
    
     // 移除 localStorage ‘myCat’ 数据项
     localStorage.removeItem('myCat');
    
     // 移除所有的 localStorage 数据项
     localStorage.clear();
    
  • storage事件
    当存储数据发生变化时,会触发 storage 事件。值得特别注意的是,该事件不在导致数据变化的当前页面触发。如果浏览器同时打开一个域名下面的多个页面,当其中的一个页面改变 sessionStorage 或 localStorage 的数据时,其他所有页面的storage事件会被触发,而原始页面并不触发 storage 事件。可以通过这种机制,实现多个窗口之间的通信。(当然 ie 这个特例除外,它包含自己本身也会触发 storage 事件)。 例如修改网站主题: 页面A: // 修改 localStorage 数据 localStorage.setItem("theme", "dark");

    页面B:
    // 监听 storage 事件
    window.addEventListener("storage", (event) => {
      console.log("数据发生变化:");
      console.log("键名:", event.key);
      console.log("旧值:", event.oldValue);
      console.log("新值:", event.newValue);
      console.log("触发页面 URL:", event.url);
      console.log("存储对象:", event.storageArea);
    
      // 根据变化更新页面
      if (event.key === "theme") {
        document.body.style.backgroundColor = event.newValue === "dark" ? "#333" : "#fff";
      }
    });
    
  1. 注意事项
    • 同步存储:localStorage 是同步存储机制,这意味着每次读取或写入数据时,JavaScript 会阻塞主线程,直到操作完成。因此,在大量数据操作时可能会导致页面卡顿
    • 数据格式: localStorage 只能存储字符串类型的键值对。如果需要存储复杂对象,必须先将其序列化为 JSON 字符串
    • 共享机制:localStorage 不能跨域共享。它的数据存储和访问遵循浏览器的同源策略,即只有在相同协议、域名和端口下的页面才能共享 localStorage 数据。

3). sessionStorage

  1. 简介
    大体上和localStorage差不多,但是sessionStorage 的数据仅在当前浏览器标签页或窗口有效,关闭标签页或窗口后数据会被清除,而且 在同一个网站的不同页面之间是不共享的。sessionStorage 的数据存储范围仅限于 当前浏览器标签页或窗口。[juejin.cn/post/719730…]
  2. 注意事项
    • 感觉这个storage没啥用,对应用来临时存储一些表单数据之类的还行
    • 仅当前标签页有效

4). indexDB

  1. 简介和特点
IndexedDB 是一种底层 API,用于在客户端存储大量的结构化数据(也包括文件/二进制大型对象(blobs))。该 API 使用索引实现对数据的高性能搜索。虽然 Web Storage 在存储较少量的数据很有用,但对于存储更大量的结构化数据来说力不从心。而 IndexedDB 提供了这种场景的解决方案。
  • 键值对储存:IndexedDB内部采用对象仓库(object store)存放数据。所有类型的数据都可以直接存入,包括 JavaScript 对象。对象仓库中,数据以"键值对"的形式保存,每一个数据记录都有对应的主键,主键是独一无二的,不能有重复,否则会抛出一个错误。
  • 异步:不同于LocalStorage,IndexedDB操作是异步的。异步设计是为了防止大量数据的读写时浏览器卡死。 支持事务:IndexedDB支持事务(transaction),这意味着一系列操作步骤之中,只要有一步失败,整个事务就都取消,数据库回滚到事务发生之前的状态,不存在只改写一部分数据的情况。
  • 同源限制:IndexedDB 受到同源限制,每一个数据库对应创建它的域名。网页只能访问自身域名下的数据库,而不能访问跨域的数据库。 储存空间大:IndexedDB的储存空间非常大。
  • 支持二进制储存:IndexedDB不仅可以储存字符串,还可以储存二进制数据(ArrayBuffer 对象和 Blob 对象)
  • 持久化存储:cookie、localStorage、sessionStorage等方式存储的数据当我们清除浏览器缓存后,这些数据都会被清除掉的,而使用IndexedDB存储的数据则不会,除非手动删除该数据库
  • 支持事务:IndexedDB支持事务(transaction),这意味着一系列的操作步骤之中,只要有一步失败了,整个事务都会取消,数据库回滚的事务发生之前的状态,这和MySQL等数据库的事务类似
  1. 对应概念和操作
    • 概念

      • 仓库 objectStore
        IndexedDB没有表的概念,它只有仓库store的概念,大家可以把仓库理解为表即可,即一个store是一张表。
      • 索引index
        在关系型数据库当中也有索引的概念,我们可以给对应的表字段添加索引,以便加快查找速率。在IndexedDB中同样有索引,我们可以在创建store的时候同时创建索引,在后续对store进行查询的时候即可通过索引来筛选,给某个字段添加索引后,在后续插入数据的过成功,索引字段便不能为空。
      const transaction = db.transaction(["person"], "readonly");
      const objectStore = transaction.objectStore("person");
      const index = objectStore.index("name");
      const request = index.get("李四");
      
      request.onerror = (event) => {
          console.log("数据查询失败");
      };
      
      request.onsuccess = (event) => {
          const result = event.target.result;
          if (result) {
              console.log("Name: " + result.name);
              console.log("Age: " + result.age);
              console.log("Email: " + result.email);
          } else {
              console.log("未获得数据记录");
          }
      };
      
      • 游标cursor
        游标是IndexedDB数据库新的概念,大家可以把游标想象为一个指针,比如我们要查询满足某一条件的所有数据时,就需要用到游标,我们让游标一行一行的往下走,游标走到的地方便会返回这一行数据,此时我们便可对此行数据进行判断,是否满足条件【只能通过主键、索引、游标方式查询数据】。
      • 事务
        IndexedDB支持事务,即对数据库进行操作时,只要失败了,都会回滚到最初始的状态,确保数据的一致性。
    • 操作

      • 创建或打开数据库
        使用indexedDB.open打开数据库,open方法返回一个IDBOpenDBRequest对象,同时这是一个异步操作,open操作并不会立马打开数据库或者开启事务,我们可以通过监听request的事件来进行相应的处理。如果要打开的数据库不存在,浏览器会自动创建。open方法传入两个参数,第一个参数是数据库的名字,第二个参数是数据库的版本号。当创建一个新的数据库或者升级一个现有的数据库版本的时候,将会触发一个onupgradeneeded事件,并在事件中传入IDBVersionChangeEvent,我们可以通过event.target.result来获取到IDBDatabase对象,然后通过这个对象来进行数据库的版本升级操作。 const version = 1; // 版本主要用来控制数据库的结构,当数据库结构(表结构)发生变化时,版本也会变化 const dbName = "myData"; const request = window.indexedDB.open(dbName, version);

           let db; // 全局 IndexedDB 数据库实例
        
           // onupgradeneeded 在版本改变时触发(首次连接数据库时,版本从 0 变成 1,因此也会触发,且先于 onsuccess)
           request.onupgradeneeded = (event) => {
           db = event.target.result;
           };
        
           // onsuccess 在连接成功后触发
           request.onsuccess =  (event) => {
               db = request.result;
               console.log("db connected");
           };
        
           // onerror 在连接失败时触发
           request.onblocked =  (event) => {
               console.log("db request blocked!");
           }
        
           // onblocked 在连接被阻止的时候触发,比如打开版本低于当前存在的版本
           request.onerror =  (event) => {
               console.log("error!");
           };
        
      • 创建数据表
        主键(key)是默认建立索引的属性,既可以让IndexedDB自动生成主键,也可以使用数据记录里面合适的属性作为主键

        image.png

      • CRUD操作

        • 需要在事务里面进行操作,并且创建事务的时候还要指定表格名称和操作模式。
        • 新建事务以后,通过IDBTransaction.objectStore(name)方法,拿到 IDBObjectStore 对象,再通过add、get、put、delete进行对应的操作,这些操作本身是异步的,所以要有onerror、onsuccess进行回调的监听
  2. 使用场景 浏览器端需要存储大量数据的场景:
  • 文件存储(图片、视频等)
  • 客户端日志存储
  • 离线应用

文章参考:
[1] 初识IndexedDB本地存储
[2] 前端本地存储数据库IndexedDB完整教程
[3] SessionStorage、LocalStorage详解
[4] HTML5本地存储——IndexedDB(二:索引)