忘记 localStorage 吧,indexedDB 才是前端存储新宠!

news2024/11/14 16:57:49

前言

在项目开发过程中,前端需要存储大量的数据。cookie, localstorage 都有存储长度限制。
表格一览

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

需要一个存储容量大,支持搜索和自定义索引的前端存储方案,就选用了 。
caniuse上查看 indexedDB 支持情况,目前浏览器支持情况良好。
caniuse.com/?search=ind…

IndexedDB介绍

 

IndexedDB 属于非关系型数据库。(不支持SQL查询)

特点:

  • 键值对储存 IndexedDB 内部采用对象仓库(object store)存放数据。所有类型的数据都可以直接存入,包括 JavaScript 对象。对象仓库中,数据以"键值对"的形式保存,每一个数据记录都有对应的主键,主键是独一无二的,不能有重复,否则会抛出一个错误。
  • 异步 IndexedDB 操作时不会锁死浏览器,用户依然可以进行其他操作,这与 LocalStorage 形成对比,后者的操作是同步的。异步设计是为了防止大量数据的读写,拖慢网页的表现。
  • 支持事务 IndexedDB 支持事务(transaction),这意味着一系列操作步骤之中,只要有一步失败,整个事务就都取消,数据库回滚到事务发生之前的状态,不存在只改写一部分数据的情况。
  • 同源限制 IndexedDB 受到同源限制,每一个数据库对应创建它的域名。网页只能访问自身域名下的数据库,而不能访问跨域的数据库。
  • 支持二进制储存 IndexedDB 不仅可以储存字符串,还可以储存二进制数据(ArrayBuffer 对象和 Blob 对象。
  • 储存空间大 IndexedDB 的储存空间比 LocalStorage 大得多,一般来说不少于 250MB,甚至没有上限。储 存 在 电 脑 上 中 的 位 置 为 C:\Users\当 前 的 登 录 用 户\AppData\Local\Google\Chrome\User Data\Default\IndexedDB

核心概念

  • 数据库:IDBDatabase 对象,数据库有版本概念,同一时刻只能有一个版本,每个域名可以建多个数据库
  • 对象仓库:IDBObjectStore 对象,类似于关系型数据库的表格
  • 索引: IDBIndex 对象,可以在对象仓库中,为不同的属性建立索引,主键建立默认索引
  • 事务: IDBTransaction 对象,增删改查都需要通过事务来完成,事务对象提供了error,abord,complete三个回调方法,监听操作结果
  • 操作请求:IDBRequest 对象
  • 指针: IDBCursor 对象
  • 主键集合:IDBKeyRange 对象,主键是默认建立索引的属性,可以取当前层级的某个属性,也可以指定下一层对象的属性,还可以是一个递增的整数

indexedDB使用

基础操作

1. 创建数据库 & 新建表和索引

/*
 *@databaseName 数据仓库的名字
 *@version 数据仓库的版本
 *@databaseName 数据仓库的名字
 */

var request = window.indexedDB.open('group', 1);

/*
 *数据仓库打开失败
 */
request.onerror = function(error) {
  console.log('IndexedDB 打开失败', error);
};

/*
 *数据仓库打开成功
 */
request.onsuccess = function(res) {
  console.log('IndexedDB 打开成功', res);
  db = res.target.result;
};

/*
 *数据仓库升级事件(第一次新建库是也会触发,因为数据仓库从无到有算是升级了一次)
 */
request.onupgradeneeded = function(res) {
  console.log('IndexedDB 升级成功', res);
  db = res.target.result;
  db_table = db.createObjectStore('group', { keyPath: 'id' });
  db_table.createIndex('indexName', 'name', { unique: false });
};

 

 

2. 新增数据

/*
 *新建事务
 *@params 数据仓库的数组
 *@params 写入模式
 */
var store = db.transaction(['group'], 'readwrite').objectStore('group');

/*
 *add方法添加数据
 *@params 需要添加的数据信息
 */
var request = store.add({
  id: new Date().getTime(),
  name: '王二',
  age: 12,
  email: 'XXXX@xxx.com',
});

/*
 *添加成功
 */
request.onsuccess = function(event) {
  console.log('数据添加成功', event);
};

/*
 *添加失败
 */
request.onerror = function(event) {
  console.log('数据添加失败', event);
};

3. 读取数据


/*
 *新建事务
 *@params 数据仓库的数组
 */
var store = db.transaction(['group']).objectStore('group');

/*
 *get方法获取数据
 *@params 数据的主键
 */
var request = store.get(1678664831491);

/*
 *获取成功
 */
request.onsuccess = function(event) {
  if (event.target.result) {
    console.log('数据获取成功', event.target.result);
  } else {
    console.log('未获取到数据');
  }
};

/*
 *获取失败
 */
request.onerror = function(event) {
  console.log('数据获取失败', event);
};

4. 更新数据

/*
 *新建事务
 *@params 数据仓库的数组
 *@params 写入模式
 */
var store = db.transaction(['group'], 'readwrite').objectStore('group');

/*
 *put方法根据主键更新数据
 *@params 数据的主键
 */
var request = store.put({
  id: 1678664831491,
  name: '张一' + Math.random(),
  age: 24,
  email: 'zhangsan@example.com',
});

/*
 *更新成功
 */
request.onsuccess = function(event) {
  console.log('数据更新成功', event);
};

/*
 *更新失败
 */
request.onerror = function(event) {
  console.log('数据更新失败', event);
};


未加 readwrite, 会抛错,修改数据失败

5. 删除数据

/*
*新建事务
*@params 数据仓库的数组
*/
var store = db.transaction(['group'], 'readwrite').objectStore('group');

/*
*delete方法删除数据
*@params 数据的主键
*/
var request = store.delete(1678664831491); 

/*
*删除成功
*/
request.onsuccess = function (event) {
    console.log('数据删除成功',event);
};

/*
*删除失败
*/
request.onerror = function (event) {
    console.log('数据删除失败',event);
};

6. 使用索引

/*
*新建事务
*@params 数据仓库的数组
*/
var store = db.transaction(['group']).objectStore('group');

/*
*index方法获取索引对象
*get方法获取数据
*@params 数据的索引
*/
var request = store.index('indexName').get('张四'); 

/*
*获取成功
*/
request.onsuccess = function (event) {
     console.log('通过索引获取数据成功',event.target.result);
};

/*
*获取失败
*/
request.onerror = function (event) {
    console.log('通过索引获取数据失败',event);
};

7. 获取整张表所有的data


var store = db.transaction(['group']).objectStore('group');
var request = store.getAll();

/*
 *更新成功
 */
request.onsuccess = function(event) {
  console.log('indexedDB getAll:', event.target.result);
};

/*
 *更新失败
 */
request.onerror = function(event) {
  console.log('indexedDB getAll:', event);
};

8. 根据指定条件获取data

首先让我们 来了解 IDBKeyRange 的API
www.w3cschool.cn/javascript_…


var store = db.transaction(['group']).objectStore('group');
// 获取id名称小于当前时间的所有data
var request = store.getAll(IDBKeyRange.upperBound(+new Date()));

/*
 *更新成功
 */
request.onsuccess = function(event) {
  console.log('indexedDB getAll:', event.target.result);
};

/*
 *更新失败
 */
request.onerror = function(event) {
  console.log('indexedDB getAll:', event);
};

业务中优雅使用

indexedDB 并非无底洞,可以无限存储。要考虑做定期删除等功能

1. 定期删除失效数据

1. 首先我们创建数据的时候就以时间戳+失效时间来约定id规则

2. 再通过上面基础操作的getAll方法,获取指定条件的data,再遍历data,调用删除数据API

var store = db.transaction(['group'], 'readwrite').objectStore('group');
var request = store.getAll(IDBKeyRange.upperBound(+new Date()));

/*
 *更新成功
 */
request.onsuccess = function(event) {
  console.log('indexedDB getAll:', event);
  console.log('indexedDB getAll:', event.target.result);
  const data = event.target.result;
  data.forEach(item => {
    console.log('删除数据', item);
    const deletRequest = store.delete(item.id);
    /*
     *删除成功
     */
    deletRequest.onsuccess = function(event) {
      console.log('数据删除成功', event);
    };


    /*
     *删除失败
     */
    deletRequest.onerror = function(event) {
      console.log('数据删除失败', event);
    };
  });
};


/*
 *更新失败
 */
request.onerror = function(event) {};

3. 我们把上述方法包装下每次打开页面,清空下失效数据。就可以实现一个定期删除失效数据的方法啦

2. 批量添加数据


const TestData = [
    {
      event: 'NE-TEST1',
      level: 'warning',
      errorCode: 200,
      url: 'http://www.example.com',
      time: '2017/11/8 下午4:53:039',
      isUploaded: false
    },
    {
      event: 'NE-TEST2',
      msg: '测试2',
      level: 'error',
      errorCode: 1000,
      url: 'http://www.example.com',
      time: '2017/11/8 下午4:53:042',
      isUploaded: false
    },
    {
      event: 'NE-TEST3',
      msg: '测试3',
      level: 'info',
      errorCode: 3000,
      url: 'http://www.example.com',
      time: '2017/11/8 下午4:53:043',
      isUploaded: false
    },
    {
      event: 'NE-TEST4',
      mgs: '测试4',
      level: 'info',
      url: 'http://www.example.com',
      time: '2017/11/8 下午4:53:0423',
      isUploaded: false
    }
  ]

/**
* 添加数据
* @param {array} docs 要添加数据
* @param {string} objName 仓库名称
*/
function addData (docs, objName) {
    if (!(docs && docs.length)) {
      throw new Error('docs must be a array!')
    }
    return openIndexedDB().then(db => {
      const tx = db.transaction([objName], 'readwrite')
      tx.oncomplete = e => {
        console.log('tx:addData onsuccess', e)
        return Promise.resolve(docs)
      }
      tx.onerror = e => {
        e.stopPropagation()
        console.error('tx:addData onerror', e.target.error)
        return Promise.reject(e.target.error)
      }
      tx.onabort = e => {
        console.warn('tx:addData abort', e.target)
        return Promise.reject(e.target.error)
      }
      const obj = tx.objectStore(objName)
      docs.forEach(doc => {
        const req = obj.add(doc)
        /**
         * NOTE:
         * request
         * 两个事件:
         * 1. success
         * 2. error
         */
        // req.onsuccess = e => console.log('obj:addData onsuccess', e.target)
        req.onerror = e => {
          console.error('obj:addData onerror', e.target.error)
        }
      })
    })
  }

  addData(TestData, OB_NAMES.UseKeyGenerator)
.then(() => addData(TestData, OB_NAMES.UseKeyPath))

结尾

一些封装好的库

localforage 推荐 ⭐️⭐️⭐️⭐️⭐️ (我们当前业务就用的这个~)

localsotrage使用保持一致,更适合前端使用
还在用localStorage?快来试试localForage吧! - 掘金

IndexedDBWrapper 推荐 ⭐️⭐️⭐️

思考我们还可以用indexedDB做什么

1. 用户使用日志收集

在做一些前端electron应用,webApp,我们可以定义一个log日志库,来收集用户日志,遇到问题时,可以让用户,打包上传到日志库,排查跟进解决用户反馈问题。

定义日志上报结构

// 定义log基本结构
const LogItem = {
	level: 'log' | 'info' | 'error' ...,
  tag: 'request' | 'system' | ‘video’ | 'audio' | 'domClick' ... ,
	msg: ...,  // any
  date: +new Date(),
	...
}

导出所有数据,并上传

此处就可以用上面的 getAll 方法,获取该表所有数据,生成json打包上传到自己公司的日志库。

2. request层封装,对不长更新接口缓存

封装request方法,缓存请求接口,物理缓存数据~让页面接口数据加载飞起来

3. 大文件上传,分片,避免网络失败,刷新页面等导致中断问题

文件切片后先存储到indexedDB库,动态更新上传状态,异常状况可取出再继续定位到未上传的切片继续上传。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/659527.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

带货、文案策划看过来-让GPT30秒内帮你写出世界顶级文案

开篇 在这个充满竞争的市场环境中,一句响亮的品牌短语,往往能让消费者对你的品牌印象深入脑海。俗话说:“言短意赅,反而显得深邃。”这不正是品牌短语的形式特点吗?而这种言简意赅、朗朗上口的品牌短语,在中国市场上就显得尤为重要。因为它直击心灵,与消费者之间一夕之…

Fiddler 八个实用技巧

大家对Fiddler应该不会陌生,但里面有些技巧不见得都会,这里就有八个实用技巧,通过对Fiddler的定制,能提高大家的测试效率。 1、双击Session时,使响应页始终显示到”json”tab页;使请求页始终显示到“webfo…

【LLM GPT】李宏毅大型语言模型课程

目录 1 概述1.1 发展历程1.2 预训练监督学习预训练的好处 1.3 增强式学习1.4 对训练数据的记忆1.5 更新参数1.6 AI内容检测1.7 保护隐私1.8 gpt和bert穷人怎么用gpt 2 生成式模型2.1 生成方式2.1.1 各个击破 Autoregressive2.1.2 一次到位 Non-autoregressive2.1.3 两者结合 2.…

js包管理yarn与npm,yarn安装,yarn 不是内部或外部命令

目录 yarn与npm 优势 用法区别 安装yarn 报错 yarn 不是内部或外部命令 运行代码(yarn dev) yarn与npm yarn由Facebook为解决npm的一些问题而创建的 优势 快速 本地缓存并行下载 - Yarn并行下载,还可以直接从硬盘缓存中读取包&…

python3 爬虫相关学习8:python 的常见报错内容汇总(持续收集ing)

目录 1 低级错误(比如拼写错误等) 1.1 NameError: 1.2 属性错误 AttributeError: 属性拼写错误 2 应用错误(类型应用,属性使用的错误) 2.1 类型错误 TypeError: 如字符串连接错误 2.2 属性应用错误 Attribu…

【枚举区间思想+DP】子串的子序列

F-子串的子序列_牛客小白月赛62 (nowcoder.com) 题意: 思路: 复盘一下应该有的思路: 首先n^2枚举肯定超时,我们枚举的是一个区间 枚举区间有一些trick: 1.枚举其中一个右(左)端点&#xff…

基于simulink车辆动力学可视化仿真(附源码)

一、前言 车辆动力学是研究汽车在行驶过程中的运动学和力学特性的学科。它研究车辆在不同路面条件、不同驾驶情况下的加速、制动、转向等运动状态,并通过建立数学模型来分析和优化车辆的性能和安全性。车辆动力学是汽车工程、机械工程和物理学等学科的交叉领域&…

【C语言11】文件操作(fgtec,fputc,fgets,fputs,fscanf,fprintf)

1.什么是文件 磁盘上的文件是文件。 但是在程序设计中,我们一般谈的文件有两种:程序文件、数据文件(从文件功能的角度来分类的)。 程序文件 包括源程序文件(后缀为.c),目标文件(windows环境后…

unittest教程__expectedFailure预期用例失败(5)

在断言用例执行结果时,会出现预期结果与实际结果不一致的情况,此时我们明确知道用例执行结果为FAIL,不想看到打印错误信息怎么办? 使用装饰器unittest.expectedFailure标记该用例。 import unittestclass Demo(unittest.TestCas…

Tcl常用命令备忘录-format与scan

format 语法:format formatString arg arg ... 参数介绍: formatString:格式化字符串,使用各种标识符和修饰符进行格式化。arg:被格式化的变量或字符串。 Tcl语言中的format命令可以用来格式化字符串输出&#xff…

Autosar代码解析-Source Insight的使用

文章目录 一、Source Insight简介二、Autosar代码查看三、解决办法一、Source Insight简介 Source insight是一款简洁、强大的轻量级代码浏览编辑器,启动快、使用便捷,很多程序员应该都使用过!它几乎支持所有的语言,比如C、C++、ASMA汇编、HTML等,能创建并维护其高性能的符…

【软件测试】性能测试服务端—排查指标问题(详细)

目录:导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜) 前言 软件性能测试的目…

【数据类型】C#和Sql Server、Mysql、Oracle等常见数据库的数据类型对应关系

🏆🏆这是小5写的第二篇城市领跑者文章,一起为所在城市领跑助力吧! 🏆🏆在实际项目中,不管是用C#后端编程语言也好,还是Java后端编程语言,都可能会用到不同端的数据类型转…

JAVA-MAVEN初学者教程(配置、pom.xml、依赖管理等)

目录 认识MAVEN安装&配置MAVENwindows安装MAVENMAVEN的配置本地仓库 localRepository镜像 mirrors代理仓库 respositories代理 proxies IDEA配置MAVEN(一个module) MAVEN生命周期install下载包 模块的pom.xml坐标gav打包方式 package属性值 properti…

2023最全Java面试八股(涵盖所有Java核心面试知识点),立刻收藏

2022已成为过去式,不论这一年好与坏,我们都需要抓住新一年的机会,跳槽涨薪,还是学习提升!先给自己定一个小目标,然后再朝着目标去努力就完事儿了! 为了帮大家节约时间,给大家搞来了2…

9.反射和Lambda表达式

目录 一、反射 1.反射是什么 2.反射操作的四个核心类(都处在java.lang.reflect) 3.在java中获取一个类的class对象一共有三种途径: 4.想通过反射来操作类或者对象,第一步就是要获得该类的class对象。 5.Constructor应用 6.Met…

LabVIEW开发光线追踪可视化分段反射器测试台

LabVIEW开发光线追踪可视化分段反射器测试台 为了满足美国国家航空航天局(NASA)对未来望远镜的要求,新的红外空间天文台将在哈勃太空望远镜使用寿命结束后取代其。作为HST的继任者,詹姆斯韦伯太空望远镜(JWST&#xf…

零基础小白如何快速入门网络安全/Web安全?

前言 网络安全现在是朝阳行业,缺口是很大。不过网络安全行业就是需要技术很多的人达不到企业要求才导致人才缺口大 初级的现在有很多的运维人员转网络安全,初级也会慢慢的卷起来,但是岗位多不用怕,以后各大厂也都会要网络安全人…

scratch lenet(6): feature_map可视化的C语言实现

scratch lenet(6): feature_map可视化的C语言实现 文章目录 scratch lenet(6): feature_map可视化的C语言实现1. 目的2. FeatureMap 的归一化2.1 公式2.2 代码实现2.3 代码调用 3. 可视化结果4. References 1. 目的 将卷积层(Convolution)、下采样层(SubSampling&a…

Linux系统之安装Ward服务器监控工具

Linux系统之安装Ward服务器监控工具 一、Ward介绍1.1 Ward简介1.2 Ward特点 二、本地环境介绍2.1 本地环境规划2.2 本次实践介绍 三、部署java环境3.1 jdk下载地址3.2 解压jdk安装包3.3 复制二进制文件3.4 配置环境编辑3.5 查看java版本 四、下载ward的jar包4.1 下载软件包4.2 …