玩转IndexedDB,比localStorage、cookie还要强大的网页端本地缓存
随着浏览器的功能不断增强,越来越多的网站开始考虑,将大量数据储存在客户端,这样可以减少从服务器获取数据,直接从本地获取数据。
现有的浏览器数据储存方案,都不适合储存大量数据:Cookie 的大小不超过 4KB,且每次请求都会发送回服务器;LocalStorage 在 2.5MB 到 10MB 之间(各家浏览器不同),而且不提供搜索功能,不能建立自定义的索引。所以,需要一种新的解决方案,这就是 IndexedDB 诞生的背景。
通俗地说,IndexedDB 就是浏览器提供的本地数据库,它可以被网页脚本创建和操作。IndexedDB 允许储存大量数据,提供查找接口,还能建立索引。这些都是 LocalStorage 所不具备的。就数据库类型而言,IndexedDB 不属于关系型数据库(不支持 SQL 查询语句),更接近 NoSQL 数据库。
对比 | cookie | localStorage | sessionStorage | indexedDB |
---|---|---|---|---|
存储大小 | 4kb | 5M | 5M | 很多于 250MB,甚至没有上限 |
与服务器端通讯 | 每次都会携带在HTTP头中,若是使用cookie保存过多数据会带来性能问题 | 仅在客户端(即浏览器)中保存,不参与和服务器的通讯 | ||
生命周期 | 通常由服务器生成,可设置失效时间。若是在浏览器端生成Cookie,默认是关闭浏览器后失效 | 除非被清除,不然永久保存 | 仅在当前会话下有效,关闭页面或浏览器后被清除 | 除非被清除,不然永久保存 |
使用场景 | 判断用户是否登陆 | 存储一些内容稳定的资源。好比图片内容丰富的电商网站会用它来存储 Base64 格式的图片字符串 | 存储一些当前会话的信息,好比微博的 sessionStorage就主要是存储你本次会话的浏览足迹 | 和 localStorage 用途相似: 1. 存储量会更大 2. localStorage使用简单字符串键值对在本地存储数据,而indexedDB能够存储任意类型的值(适合键值对较多的数据,若是使用 localStorage 存储每次都要写入,写出须要字符串化和对象化) 复制代码 |
目前,Chrome 27+、Firefox 21+、Opera 15+和IE 10+支持这个API,但是Safari完全不支持。
下面的代码用来检查浏览器是否支持这个API。
if("indexedDB" in window) {// 支持
} else {// 不支持
}
IndexedDB 具有以下特点。
(1)键值对储存。 IndexedDB 内部采用对象仓库(object store)存放数据。所有类型的数据都可以直接存入,包括 JavaScript 对象。对象仓库中,数据以“键值对”的形式保存,每一个数据记录都有对应的主键,主键是独一无二的,不能有重复,否则会抛出一个错误。
(2)异步。 IndexedDB 操作时不会锁死浏览器,用户依然可以进行其他操作,这与 LocalStorage 形成对比,后者的操作是同步的。异步设计是为了防止大量数据的读写,拖慢网页的表现。
(3)支持事务。 IndexedDB 支持事务(transaction),这意味着一系列操作步骤之中,只要有一步失败,整个事务就都取消,数据库回滚到事务发生之前的状态,不存在只改写一部分数据的情况。
(4)同源限制。 IndexedDB 受到同源限制,每一个数据库对应创建它的域名。网页只能访问自身域名下的数据库,而不能访问跨域的数据库。
(5)储存空间大。 IndexedDB 的储存空间比 LocalStorage 大得多,一般来说不少于 250MB,甚至没有上限。
(6)支持二进制储存。 IndexedDB 不仅可以储存字符串,还可以储存二进制数据(ArrayBuffer 对象和 Blob 对象)。
IndexedDB 是一个比较复杂的 API,涉及不少概念。它把不同的实体,抽象成一个个对象接口。学习这个 API,就是学习它的各种对象接口。
- 数据库:IDBDatabase 对象
- 对象仓库:IDBObjectStore 对象
- 索引: IDBIndex 对象
- 事务: IDBTransaction 对象
- 操作请求:IDBRequest 对象
- 指针: IDBCursor 对象
- 主键集合:IDBKeyRange 对象
下面是一些主要的概念。
(1)数据库
数据库是一系列相关数据的容器。每个域名(严格的说,是协议 + 域名 + 端口)都可以新建任意多个数据库。
IndexedDB 数据库有版本的概念。同一个时刻,只能有一个版本的数据库存在。如果要修改数据库结构(新增或删除表、索引或者主键),只能通过升级数据库版本完成。
(2)对象仓库
每个数据库包含若干个对象仓库(object store)。它类似于关系型数据库的表格。
(3)数据记录
对象仓库保存的是数据记录。每条记录类似于关系型数据库的行,但是只有主键和数据体两部分。主键用来建立默认的索引,必须是不同的,否则会报错。主键可以是数据记录里面的一个属性,也可以指定为一个递增的整数编号。
{ id: 1, value: '对应的值' }
上面的对象中,id
属性可以当作主键。
数据体可以是任意数据类型,不限于对象。
(4)索引
为了加速数据的检索,可以在对象仓库里面,为不同的属性建立索引。
(5)事务
数据记录的读写和删改,都要通过事务完成。事务对象提供error
、abort
和complete
三个事件,用来监听操作结果。
用例代码
<template><div><el-input v-model.trim="databaseName" :placeholder="`请输入数据库名称`" /><el-input v-model.trim="tableName" :placeholder="`请输入表名称`" /><el-input v-model.trim="index" :placeholder="`请输入字段名`" /><el-button type="primary" @click="btn1()">创建数据库</el-button><hr><el-input v-model.trim="value" :placeholder="`请输入值`" /><el-button type="success" @click="btn2()">添加数据</el-button><hr><el-input v-model.trim="keyPathValue" :placeholder="`请输入主键值`" /><el-input v-model.trim="newValue" :placeholder="`请输入修改值`" /><el-button type="warning" @click="btn3()">修改数据</el-button><hr><el-button type="info" @click="btn4()">读取数据</el-button><template v-if="tableData.length"><el-button type="danger" @click="btn5">删除全部</el-button><el-table :data="tableData"><el-table-column :prop="keyPath" :label="keyPath" /><el-table-column :prop="index" :label="index" /><el-table-column label="操作"><template slot-scope="scope"><el-button size="mini" type="danger" @click.stop="btn6(scope.row[keyPath])">删除</el-button></template></el-table-column></el-table></template></div>
</template>
<script>
export default {data() {return {databaseName: '',tableName: '',keyPath: 'id',keyPathValue: '',index: '',value: '',newValue: '',tableData: [],}},methods: {// 用例----------------------------------------// 创建btn1() {let databaseName = this.databaseName;let version = this.version;let tableName = this.tableName;let keyPath = this.keyPath;let indexs = [[this.index, this.index, { unique: false }],];//如需定义多格字段,就多几个数组// 创建表this.creatDatabaseTable({databaseName, version,tableName,//定义表名keyPath,//定义主键indexs,// 定义索引字段});},// 添加btn2() {let databaseName = this.databaseName;let version = this.version;let tableName = this.tableName;let keyPath = this.keyPath;let index = this.index;this.addData({databaseName, version, tableName,data: {[keyPath]: '********'.replace(/\*/g, () => Math.round(Math.random() * 15).toString(16)),//随机id[index]: this.value,},onsuccess: d => { console.log(`onsuccess`, d); },onerror: d => { console.log(`onerror`, d); },})},// 修改btn3() {let databaseName = this.databaseName;let version = this.version;let tableName = this.tableName;let keyPath = this.keyPath;let index = this.index;this.updateData({databaseName, version, tableName,data: {[keyPath]: this.keyPathValue,[index]: this.newValue,}});this.btn4();//刷新数据},// 读取btn4() {let databaseName = this.databaseName;let version = this.version;let tableName = this.tableName;this.readData({databaseName, version, tableName,onsuccess: ({ data }) => { this.tableData = data },})},// 删除全部btn5() {let databaseName = this.databaseName;let version = this.version;let tableName = this.tableName;this.delAllData({ databaseName, version, tableName, });this.btn4();//刷新数据},// 删除btn6(id) {let databaseName = this.databaseName;let version = this.version;let tableName = this.tableName;this.delData({databaseName, version, tableName,data: id,//需要删除的数据主键onsuccess: d => {this.btn4();//刷新数据},onerror: d => { console.log(`onerror`, d); },})},// indexedDB----------------------------------------// 1、创建or打开客户端数据库createDatabase({ databaseName, version = 1, onupgradeneeded, onsuccess, onerror } = {}) {let request = window.indexedDB.open(databaseName, version);request.onupgradeneeded = onupgradeneeded;request.onsuccess = onsuccess;request.onerror = onerror;},getDatabase(obj) { return this.createDatabase(obj); },//获取数据库// 2、创建表creatDatabaseTable({ databaseName, version, tableName, keyPath, indexs, onupgradeneeded, onsuccess, onerror } = {}) {this.getDatabase({databaseName, version,onupgradeneeded: d => {let database = d.target.result;if (!database.objectStoreNames.contains(tableName)) {//createObjectStore只能在onupgradeneeded里面执行let objectStore = database.createObjectStore(tableName, { keyPath });(indexs || []).forEach(v => objectStore.createIndex(...v));onupgradeneeded && onupgradeneeded({ event: d, objectStore });}},onsuccess,onerror,})},// 3、添加数据or修改数据or删除数据addData({ databaseName, version, tableName, data, onsuccess, onerror, triggerName = 'add' } = {}) {this.getDatabase({databaseName, version,onsuccess: d => {let database = d.target.result;let objectStore = database.transaction(tableName, 'readwrite').objectStore(tableName);let request_objectStore = objectStore.get(data[objectStore.keyPath]);request_objectStore.onsuccess = event => {event.target.result && (triggerName = 'put');//如果已经存在该主键数据,就变成修改let request = objectStore[triggerName](data);request.onsuccess = onsuccess;request.onerror = onerror;};},})},// 4、修改数据updateData(obj) { this.addData({ ...obj, triggerName: 'put' }) },// 5、删除数据delData(obj) { this.addData({ ...obj, triggerName: 'delete' }) },delAllData({ databaseName, version, tableName } = {}) {this.getDatabase({databaseName, version,onsuccess: d => {let objectStore = d.target.result.transaction(tableName, 'readwrite').objectStore(tableName);objectStore.clear();},})},// 6、读取数据readData({ databaseName, version, tableName, onsuccess } = {}) {this.getDatabase({databaseName, version,onsuccess: d => {let database = d.target.result;if (database.objectStoreNames.contains(tableName)) {let objectStore = database.transaction(tableName).objectStore(tableName);let data = [];objectStore.openCursor().onsuccess = event => {let cursor = event.target.result;if (cursor) {data.push(cursor.value); cursor.continue();} else {onsuccess && onsuccess({ event, data });// console.log('没有更多数据了!'); }};} else onsuccess && onsuccess({ msg: '表格不存在!', data: [] });},})},// ----------------------------------------}
};
</script>
相关文章:

玩转IndexedDB,比localStorage、cookie还要强大的网页端本地缓存
随着浏览器的功能不断增强,越来越多的网站开始考虑,将大量数据储存在客户端,这样可以减少从服务器获取数据,直接从本地获取数据。 现有的浏览器数据储存方案,都不适合储存大量数据:Cookie 的大小不超过 4K…...

RedisDesktopManager连不上redis问题解决(小白版)
常见问题就是 redis.conf配置文件 a.将port 127.0.0.1这一行注释掉 b.protected-mode保护模式改为no 这个可以看到很多博主都说了,相信都搜到这里来了你们都弄了,我就不详细说了 防火墙开放端口 我说明我自己的问题以及解决方法 1、执行telnet 虚拟…...

蓝帽杯 取证2022
网站取证 网站取证_1 下载附件 并解压 得到了一个文件以及一个压缩包 解压压缩包 用火绒查病毒 发现后门 打开文件路径之后 发现了一句话木马 解出flag 网站取证_2 让找数据库链接的明文密码 打开www文件找找 查看数据库配置文件/application/database.php(CodeI…...
MyBatis and or使用列表控制or条件
背景:最近项目需要,师傅可以查找订单,而师傅是指定可以服务2到3个区域,故需要使用到and, or条件的组合,以下记一下代码。 最重要的代码是: 1、构建List<Consumer<LambdaQueryWrapper<T>>&g…...

C语言刷题训练【第11天】
大家好,我是纪宁。 今天是C语言笔试刷题训练的第11天,加油! 文章目录 1、声明以下变量,则表达式: ch/i (f*d – i) 的结果类型为( )2、关于代码的说法正确的是( )3、已知有如下各变…...

正则表达式的使用
1、正则表达式-教程 正则表达式:文本模式,包括普通字符(例如,a到z之间的字母)和特殊字符(称为元字符)。 正则表达式使用单个字符串来描述,匹配一系列匹配某个句法规则的字符串。 2、…...

PHP 求解两字符串所有公共子序列及最长公共子序列 支持多字节字符串
/*** 获取两字符串所有公共子序列【不连续的】 例:abc ac > ac** param string $str1 字符串1* param string $str2 字符串2** return array*/ function public_sequence(string $str1, string $str2): array {$data [[-1, -1, , 0, ]]; // 子序列容器【横坐标 …...
linux内核bitmap之setbit汇编实现
内核版本:kernel 0.12 首先看一段代码,下面这段代码来自内核版本0.12的mm/swap.c中: // mm/swap.c #define bitop(name,op) \static inline int name(char * addr,unsigned int nr) \ { \int __res; \__asm__ __volatile__("bt" …...
Golang设计模式
Golang设计模式 Golang设计模式简介Golang工厂设计模式Golang单例设计模式Golang抽象工厂设计模式Golang建造者模式 (Builder Pattern)Golang 原型模式(Prototype Pattern)Golang适配器模式Golang 桥接模式(Bridge Pattern)Golang装饰器模式(Decorator …...
leetcode151. 反转字符串中的单词
题目:leetcode151. 反转字符串中的单词 描述: 给你一个字符串 s ,请你反转字符串中 单词 的顺序。 单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。 返回 单词 顺序颠倒且 单词 之间用单个空格连接的结…...

【BASH】回顾与知识点梳理(十七)
【BASH】回顾与知识点梳理 十七 十七. 什么是 Shell scripts17.1 干嘛学习 shell scripts自动化管理的重要依据追踪与管理系统的重要工作简单入侵检测功能连续指令单一化简易的数据处理跨平台支持与学习历程较短 17.2 第一支 script 的撰写与执行撰写第一支 script 17.3 撰写 s…...

时序预测-Informer简介
文章目录 Informer介绍1. Transformer存在的问题2. Informer研究背景3. Informer 整体架构3.1 ProbSparse Self-attention3.2 Self-attention Distilling3.3 Generative Style Decoder 4. Informer的实验性能5. 相关资料 Informer介绍 1. Transformer存在的问题 Informer实质…...
2023牛客第七场补题报告C F L M
2023牛客第七场补题报告C F L M C-Beautiful Sequence_2023牛客暑期多校训练营7 (nowcoder.com) 思路 观察到数组一定是递增的,所以从最高位往下考虑每位的1最多只有一个,然后按位枚举贪心即可。 代码 #include <bits/stdc.h> using namespac…...

Android使用kotlin+协程+room数据库的简单应用
前言:一般主线程(UI线程)中是不能执行创建数据这些操作的,因为等待时间长。所以协程就是为了解决这个问题出现。 第一步:在模块级的build.gradle中引入 id com.android.application// roomid kotlin-androidid kotlin…...

Kubernetes pod调度约束[亲和性 污点] 生命阶段 排障手段
调度约束 Kubernetes 是通过 List-Watch 的机制进行每个组件的协作,保持数据同步的,每个组件之间的设计实现了解耦。 用户是通过 kubectl 根据配置文件,向 APIServer 发送命令,在 Node 节点上面建立 Pod 和 Container。 APIServer…...
Matlab实现模拟退火算法(附上多个完整源码)
模拟退火算法(Simulated Annealing)是一种全局优化算法,其基本思想是通过模拟物理退火过程来寻找最优解。该算法可以应用于各种优化问题,如函数优化、组合优化、图形优化等。 文章目录 步骤简单案例完整仿真源码下载 步骤 在Mat…...

前后端分离------后端创建笔记(03)前后端对接(上)
本文章转载于【SpringBootVue】全网最简单但实用的前后端分离项目实战笔记 - 前端_大菜007的博客-CSDN博客 仅用于学习和讨论,如有侵权请联系 源码:https://gitee.com/green_vegetables/x-admin-project.git 素材:https://pan.baidu.com/s/…...
stable diffusion安装包和超火使用文档及提示词,数字人网址
一:文生图、图生图 1:stable diffusion:对喜欢二次元、美女小姐姐、大眼萌妹的人及其友好哈哈(o^^o) 1):关于安装包和模型包: 链接:https://pan.baidu.com/s/11_kguofh76gwhTBPUipepw 提取码…...
训练营:贪心篇
贪心就是:局部最优 1、455. 分发饼干 按照饼干分,从大到小,最大的给胃口最大能满足的 def findContentChildren455(g, s):g sorted(g,reverseTrue)s sorted(s,reverseTrue)j0c 0i0while(i<len(s) and j<len(g)):if s[i]>g[j]:c…...

四、Dubbo扩展点加载机制
四、Dubbo扩展点加载机制 4.1 加载机制概述 Dubbo良好的扩展性与框架中针对不同场景使用合适设计模式、加载机制密不可分 Dubbo几乎所有功能组件都是基于扩展机制(SPI)实现的 Dubbo SPI 没有直接使用 Java SPI,在它思想上进行改进ÿ…...

LeetCode - 394. 字符串解码
题目 394. 字符串解码 - 力扣(LeetCode) 思路 使用两个栈:一个存储重复次数,一个存储字符串 遍历输入字符串: 数字处理:遇到数字时,累积计算重复次数左括号处理:保存当前状态&a…...

屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!
5月28日,中天合创屋面分布式光伏发电项目顺利并网发电,该项目位于内蒙古自治区鄂尔多斯市乌审旗,项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站,总装机容量为9.96MWp。 项目投运后,每年可节约标煤3670…...

NFT模式:数字资产确权与链游经济系统构建
NFT模式:数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新:构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议:基于LayerZero协议实现以太坊、Solana等公链资产互通,通过零知…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)
文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...
AspectJ 在 Android 中的完整使用指南
一、环境配置(Gradle 7.0 适配) 1. 项目级 build.gradle // 注意:沪江插件已停更,推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...

VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP
编辑-虚拟网络编辑器-更改设置 选择桥接模式,然后找到相应的网卡(可以查看自己本机的网络连接) windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置,选择刚才配置的桥接模式 静态ip设置: 我用的ubuntu24桌…...
QT3D学习笔记——圆台、圆锥
类名作用Qt3DWindow3D渲染窗口容器QEntity场景中的实体(对象或容器)QCamera控制观察视角QPointLight点光源QConeMesh圆锥几何网格QTransform控制实体的位置/旋转/缩放QPhongMaterialPhong光照材质(定义颜色、反光等)QFirstPersonC…...

GruntJS-前端自动化任务运行器从入门到实战
Grunt 完全指南:从入门到实战 一、Grunt 是什么? Grunt是一个基于 Node.js 的前端自动化任务运行器,主要用于自动化执行项目开发中重复性高的任务,例如文件压缩、代码编译、语法检查、单元测试、文件合并等。通过配置简洁的任务…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现企业微信功能
1. 开发环境准备 安装DevEco Studio 3.1: 从华为开发者官网下载最新版DevEco Studio安装HarmonyOS 5.0 SDK 项目配置: // module.json5 {"module": {"requestPermissions": [{"name": "ohos.permis…...