IndexDB 浏览器服务器
IndexDB 浏览器服务器
文章部分内容引用:
https://www.ruanyifeng.com/blog/2018/07/indexeddb.html
https://juejin.cn/post/7026900352968425486#heading-15
基本概念
- 数据库:IDBDatabase 对象
- 对象仓库:IDBObjectStore 对象
- 索引: IDBIndex 对象
- 事务: IDBTransaction 对象
- 操作请求:IDBRequest 对象
- 指针: IDBCursor 对象
- 主键集合:IDBKeyRange 对象
(1)数据库
数据库是一系列相关数据的容器。每个域名(严格的说,是协议 + 域名 + 端口)都可以新建任意多个数据库。
IndexedDB 数据库有版本的概念。同一个时刻,只能有一个版本的数据库存在。如果要修改数据库结构(新增或删除表、索引或者主键),只能通过升级数据库版本完成。
(2)对象仓库
每个数据库包含若干个对象仓库(object store)。它类似于关系型数据库的表格。
(3)数据记录
对象仓库保存的是数据记录。每条记录类似于关系型数据库的行,但是只有主键和数据体两部分。主键用来建立默认的索引,必须是不同的,否则会报错。主键可以是数据记录里面的一个属性,也可以指定为一个递增的整数编号。
{ id: 1, text: 'foo' }
上面的对象中,id
属性可以当作主键。
数据体可以是任意数据类型,不限于对象。
(4)索引
为了加速数据的检索,可以在对象仓库里面,为不同的属性建立索引。
(5)事务
数据记录的读写和删改,都要通过事务完成。事务对象提供error
、abort
和complete
三个事件,用来监听操作结果。
操作流程
IndexedDB 鼓励使用的基本模式如下所示:
- 打开数据库。
- 在数据库中创建一个对象仓库(object store)。
- 启动一个事务,并发送一个请求来执行一些数据库操作,像增加或提取数据等。
- 通过监听正确类型的 DOM 事件以等待操作完成。
- 在操作结果上进行一些操作(可以在 request 对象中找到)
打开数据库
由于 IndexedDB 本身的规范还在持续演进中,当前的 IndexedDB 的实现还是使用浏览器前缀。
在规范更加稳定之前,浏览器厂商对于标准 IndexedDB API 可能都会有不同的实现。但是一旦大家对规范达成共识的话,厂商就会不带前缀标记地进行实现。
实际上一些实现已经移除了浏览器前缀:IE 10,Firefox 16 和 Chrome 24。
当使用前缀的时候,基于 Gecko 内核的浏览器使用
moz
前缀,基于 WebKit 内核的浏览器会使用webkit
前缀。
window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
使用 IndexedDB 的第一步是打开数据库,使用indexedDB.open()
方法
var request = window.indexedDB.open(databaseName, version);
这里传入两个参数:
databaseName
:字符串,表示数据库的名字;如果指定的数据库不存在,就会新建数据库。version
:整数,表示数据库的版本,默认为当前版本;新建数据库时,默认为1
indexedDB.open()
方法返回一个IDBRequest
对象。这个对象通过三种事件error
、success
、upgradeneeded
,处理打开数据库的操作结果。
(1)error 事件
error
事件表示打开数据库失败。
request.onerror = function (event) {console.log('数据库打开报错');};
(2)success 事件
success
事件表示成功打开数据库。
var db;
request.onsuccess = function (event) {db = request.result;console.log('数据库打开成功');
};
这时,通过request
对象的result
属性拿到数据库对象。
(3)upgradeneeded 事件
如果指定的版本号,大于数据库的实际版本号,就会发生数据库升级事件upgradeneeded
。
如果 onupgradeneeded
事件成功执行完成,打开数据库请求的 onsuccess
处理函数会被触发。
var db;
request.onupgradeneeded = function (event) {db = event.target.result;
}
这时通过事件对象的target.result
属性,拿到数据库实例。
/*** 打开数据库* @param {string} dbName 数据库的名字* @param {string} version 数据库的版本* @return {object} 该函数会返回一个数据库实例*/
function openDB(dbName, storeName, version) {return new Promise((resolve, reject) => {// 兼容浏览器var indexedDB =window.indexedDB ||window.mozIndexedDB ||window.webkitIndexedDB ||window.msIndexedDB;let db;// 打开数据库,如果没有则被创建const request = indexedDB.open(dbName, (version = 1));// 数据库成功打开的回调request.onsuccess = function (event) {db = event.target.result;resolve(db);console.log("数据库成功打开");};// 数据库打开失败的回调request.onerror = function (event) {console.error("数据库打开失败");};request.onupgradeneeded = function (event) {console.log("数据库更新");db = event.target.result;// 更好的写法是先判断一下,这存储库是否存在,如果不存在再新建if (!db.objectStoreNames.contains("user")) {// 创建存储库const objectStore = db.createObjectStore("user", {keyPath: "userId", // 主键// autoIncrement:true // 实现自增});// 创建索引,在后面查询数据的时候可以根据索引查objectStore.createIndex("userid", "userId", { unique: true }); // unique是否唯一objectStore.createIndex("name", "name", { unique: false });objectStore.createIndex("age", "age", { unique: false });}};});
}
新增数据
新增数据指的是向对象仓库写入数据记录。这需要通过事务完成。
事务提供了三种模式:readonly
、readwrite
和 versionchange
。
想要修改数据库模式或结构——包括新建或删除对象仓库或索引,只能在 versionchange
事务中才能实现。
该事务由一个指定了 version 的
IDBFactory.open
方法启动。(在仍未实现最新标准的 WebKit 浏览器,IDBFactory.open
方法只接受一个参数,即数据库的name
,这样你必须调用IDBVersionChangeRequest.setVersion
来建立versionchange
事务。
使用 readonly
或 readwrite
模式都可以从已存在的对象仓库里读取记录。但只有在 readwrite
事务中才能修改对象仓库。
你需要使用
IDBDatabase.transaction
(en-US) 启动一个事务。该方法接受两个参数:storeNames
(作用域,一个你想访问的对象仓库的数组),事务模式mode
(readonly 或 readwrite)。该方法返回一个包含IDBIndex.objectStore
(en-US) 方法的事务对象,使用IDBIndex.objectStore
(en-US) 你可以访问你的对象仓库。未指定mode
时,默认为readonly
模式。
error
事件是冒泡机制,所以事务会接收由它产生的所有请求所产生的错误。更微妙的一点,错误会中断它所处的事务。除非你在错误发生的第一时间就调用了stopPropagation
并执行了其他操作来处理错误,不然整个事务将会回滚。- 如果你在事务中没有处理一个已发生的错误或者调用 abort 方法,那么该事务会被回滚,并触发 abort 事件。
- 在所有请求完成后,事务的 complete 事件会被触发。
function addData(db, storeName, data) {const request = db.transaction([storeName], "readwrite").objectStore("user").add(data || { userId: 1, name: "张三", age: 18 });request.onsuccess = function (event) {console.log("数据写入成功");};request.onerror = function (event) {console.error("数据写入失败,原因:", event.target.error);};
}
通过主键删除数据
IDBObjectStore.delete()
方法用于删除记录。
/*** * @param {IDBObjectStore} db 数据库实例* @param {string} storeName 仓库名称* @param {string} key 主键值 */function remove(db,storeName,key) {var request = db.transaction([storeName], 'readwrite').objectStore(storeName).delete(key);request.onsuccess = function (event) {console.log('数据删除成功');};
}
更新数据
put
方法接收一个数据对象。
/*** 更新数据* @param {object} db 数据库实例* @param {string} storeName 仓库名称* @param {object} data 数据*/
function updateDB(db, storeName, data) {var request = db.transaction([storeName], "readwrite") // 事务对象.objectStore(storeName) // 仓库对象.put(data);request.onsuccess = function () {console.log("数据更新成功");};request.onerror = function () {console.log("数据更新失败");};
}
通过主键读取数据
现在数据库里已经有了一些信息,你可以通过几种方法对它进行提取。首先是简单的 get()
。你需要提供键来提取值,像这样:
主键即刚刚我们在创建数据库时声明的keyPath
,通过主键只能查询出一条数据。
/*** 通过主键读取数据* @param {IDBObjectStore} db 数据库实例* @param {string} storeName 仓库名称* @param {string} key 主键值*/
function getDataByKey(db, storeName, key) {return new Promise((resolve, reject) => {var transaction = db.transaction([storeName]); // 事务var objectStore = transaction.objectStore(storeName); // 仓库对象var request = objectStore.get(key); // 通过主键获取数据request.onerror = function (event) {console.log("事务失败");};request.onsuccess = function (event) {console.error("主键查询结果: ", request.result);resolve(request.result);};});
}
通过游标查询数据
/*** 通过游标读取数据* @param {IDBObjectStore} db 数据库实例* @param {string} storeName 仓库名称*/
function cursorGetData(db, storeName) {let list = [];var store = db.transaction(storeName, "readwrite") // 事务.objectStore(storeName); // 仓库对象var request = store.openCursor(); // 指针对象// 游标开启成功,逐行读数据request.onsuccess = function (e) {var cursor = e.target.result;if (cursor) {// 必须要检查list.push(cursor.value);cursor.continue(); // 遍历了存储对象中的所有内容} else {console.log("游标读取的数据:", list);}};
}
上面函数开启了一个游标,然后逐行读取数据,存入数组,最终得到整个仓库的所有数据。
通过索引查询数据
// 首先,确定你已经在 request.onupgradeneeded 中创建了索引:
// objectStore.createIndex("name", "name");
// 否则你将得到 DOMException。var index = objectStore.index("name");index.get("Donna").onsuccess = function(event) {alert("Donna's SSN is " + event.target.result.ssn);
};
索引名称即我们创建仓库的时候创建的索引名称,也就是键值对中的键,最终会查询出所有满足我们传入函数索引值的数据。
/*** 通过索引读取数据* @param {IDBObjectStore} db 数据库实例* @param {string} storeName 仓库名称* @param {string} indexName 索引名称* @param {string} indexValue 索引值*/
function getDataByIndex(db, storeName, indexName, indexValue) {var store = db.transaction(storeName, "readwrite").objectStore(storeName);var request = store.index(indexName).get(indexValue);request.onerror = function () {console.error("事务失败");};request.onsuccess = function (e) {var result = e.target.result;console.log("索引查询结果:", result);};
}
指定游标的范围和方向
如果你想要限定你在游标中看到的值的范围,你可以使用一个 key range 对象然后把它作为第一个参数传给 openCursor()
或 openKeyCursor()
。
// 仅匹配 "Donna"
var singleKeyRange = IDBKeyRange.only("Donna");// 匹配所有超过“Bill”的,包括“Bill”
var lowerBoundKeyRange = IDBKeyRange.lowerBound("Bill");// 匹配所有超过“Bill”的,但不包括“Bill”
var lowerBoundOpenKeyRange = IDBKeyRange.lowerBound("Bill", true);// 匹配所有不超过“Donna”的,但不包括“Donna”
var upperBoundOpenKeyRange = IDBKeyRange.upperBound("Donna", true);// 匹配所有在“Bill”和“Donna”之间的,但不包括“Donna”
var boundKeyRange = IDBKeyRange.bound("Bill", "Donna", false, true);// 使用其中的一个键范围,把它作为 openCursor()/openKeyCursor 的第一个参数
index.openCursor(boundKeyRange).onsuccess = function(event) {var cursor = event.target.result;if (cursor) {// 当匹配时进行一些操作cursor.continue();}
};
删除数据库
/*** 删除数据库* @param {object} dbName 数据库名称*/
function deleteDBAll(dbName) {console.log(dbName);let deleteRequest = window.indexedDB.deleteDatabase(dbName);deleteRequest.onerror = function (event) {console.log("删除失败");};deleteRequest.onsuccess = function (event) {console.log("删除成功");};
}
相关文章:
IndexDB 浏览器服务器
IndexDB 浏览器服务器 文章部分内容引用: https://www.ruanyifeng.com/blog/2018/07/indexeddb.html https://juejin.cn/post/7026900352968425486#heading-15 基本概念 数据库:IDBDatabase 对象对象仓库:IDBObjectStore 对象索引࿱…...

追梦之旅【数据结构篇】——详解C语言实现链队列
详解C语言实现链队列~😎前言🙌整体实现内容分析💞预备小知识🙌1.链队列头文件编写🙌2.链队列功能文件(Queue.c )编写:🙌1)初始化函数实现2)销毁函…...

SpringMVC - 13 - SpringMVC执行流程
文章目录1、SpringMVC常用组件2、DispatcherServlet初始化过程a>初始化WebApplicationContextb>创建WebApplicationContextc>DispatcherServlet初始化策略3、DispatcherServlet调用组件处理请求a>processRequest()b>doService()c>doDispatch()d>processDi…...
6091: 斐波那契数列
描述一个斐波那契序列,F(0) 0, F(1) 1, F(n) F(n-1) F(n-2) (n>2),根据n的值,计算斐波那契数F(n)。输入输入数据的第一行为测试用例的个数t,接下来为t行,每行为一个整数n(2≤n≤40)。输出…...

任何人均可上手的数据库与API搭建平台
编写API可能对于很多后端开发人员来说,并不是什么难事儿,但如果您主要从事前端功能,那么可能还是有一些门槛。 那么有没有工具可以帮助我们降低编写API的学习门槛和复杂度呢? 今天就来给大家推荐一个不错的开源工具:…...

Ubuntu(虚拟机)的Anaconda 及使用
安装Anaconda 使用firefox打开Ananconda网址Anaconda | The Worlds Most Popular Data Science Platform 下载后有.sh文件: Anaconda3-2022.10-Linux-x86_64.sh 进入所在目录打开终端并输入 $ bash Anaconda3-2022.10-Linux-x86_64.sh 然后开始安装。 对于给…...

Git ---- IDEA集成 GitHub
Git ---- IDEA集成 GitHub1. 设置 GitHub 账号2. 分享工程到 GitHub3. push 推送本地库到远程库4. pull 拉取远程库到本地库5. clone 克隆远程库到本地1. 设置 GitHub 账号 新版的 IDEA 选择之后会自动登录,就不需要设置 token 了。 如果是老版的 IDEA 的话&…...
opencv提取结构化文本总结
扫描文件表格识别 1.识别结构 situation1 有明确表格结构 1.纠正表格偏移角度(获取最大轮廓,计算最小的矩形,变换坐标截取矩形) 获取面积最大轮廓 _, contours, HIERARCHY cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2…...

JVM知识体系学习八:OOM的案例(承接上篇博文,可以作为面试中的案例)
文章目录前言一、概述二、案例二三、案例:方法区内存溢出1、代码:LambdaGC.java2、元空间内存溢出日志3、分析4、疑问*****四、案例:直接内存溢出问题(少见)(尽量不说)五、案例:栈内存溢出问题1…...

Redis的持久化方式
Redis支持两种方式的持久化,一种是RDB方式、另一种是AOF(append-only-file)方式,两种持久化方式可以单独使用其中一种,也可以将这两种方式结合使用。 •RDB:根据指定的规则“定时”将内存中的数据存储在硬…...

【unity游戏制作-mango的冒险】-4.场景二的镜头和法球特效跟随
👨💻个人主页:元宇宙-秩沅 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 本文由 秩沅 原创 收录于专栏:unity游戏制作 ⭐mango的冒险场景二——镜头和法球特效跟随⭐ 文章目录⭐mango的冒险场景二——镜…...

handwrite-1
-------------------- 实现防抖函数(debounce) 防抖函数原理:把触发非常频繁的事件合并成一次去执行 在指定时间内只执行一次回调函数,如果在指定的时间内又触发了该事件,则回调函数的执行时间会基于此刻重新开始计算…...
【一天一门编程语言】Pascal 语言程序设计极简教程
Pascal 语言程序设计极简教程 用 markdown 格式输出答案。 不少于3000字。细分到2级目录。 文章目录 Pascal 语言程序设计极简教程一、Pascal简介1.1 Pascal历史1.2 Pascal的特点1.3 Pascal的应用二、Pascal语言程序设计2.1 Pascal编程环境2.2 Pascal的基本语法2.3 Pascal程序…...

【基础篇0】Linux下ANACONDA与TF-LITE环境配置
0 写在前面:一些摸索与总结 对于Linux系统,我发现不管是电脑x86的Ubuntu还是树莓派arm的raspberry系统,在系统安装完毕后,总是自带一个特定版本的python. 例如我的ubuntu22.04自带的python版本是3.10,而高版本的py…...

TCP协议原理二
文章目录四、滑动窗口二、流量窗口三、拥塞控制四、滑动窗口 前面我们学习了 确认应答,超时重传,连接管理,这些机制都为我们TCP的可靠性提供了保证,当然在保证TCP的可靠性的同时,传输效率也受到了一定的影响ÿ…...

电子科技大学网络协议(TCP/IP作业答案)--网工(五次作业汇总)
目录 作业1:OSI/RM、TCP/IP编址和底层网络技术 作业2:IP地址规划与路由选择 作业3:ARP、IP、ICMP 作业4:UDP、Routing Protocol 作业五 作业1:OSI/RM、TCP/IP编址和底层网络技术 物理地址属于OSI/RM的哪一层&…...
Kubernetes集群声明式文件YAML
一、YAML介绍 YAML 的意思是:仍是一种标记语言,但为了强调这种语言以数据做为中心,而不是以标记语言为重点。是一个可读性高,用来表达数据序列的格式。 二、基本语法 1.低版本缩进时不允许使用Tab键,只允许使用空格…...

为赋能,创共赢~ 〖TFS_CLUB社区〗-〖星荐官计划〗来袭~ 期待各位小伙伴的加入~
文章目录❤️🔥 TFS社区介绍❤️🔥 星荐官计划在直播结束之后,有几位小伙伴跟我说,想法是好的,但是会很难搞。试想一下如果真的是很容易做的事情,那岂不是人人都可以做?正因为难做ÿ…...

【华为OD机试模拟题】用 C++ 实现 - 水仙花数(2023.Q1)
最近更新的博客 【华为OD机试模拟题】用 C++ 实现 - 获得完美走位(2023.Q1) 文章目录 最近更新的博客使用说明水仙花数题目输入输出描述示例一输入输出说明示例二输入输出Code使用说明 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率才会高。…...

Windows作为操作系统的典型特征和主要功能
我是荔园微风,作为一名在IT界整整25年的老兵,今天我们来重新审视一下Windows这个我们熟悉的不能再熟悉的系统。我们每天都在用Windows操作系统,但是其实我们每天直接在打交道的并不是Windows操作系统的内核,而是Windows操作系统的…...

网络六边形受到攻击
大家读完觉得有帮助记得关注和点赞!!! 抽象 现代智能交通系统 (ITS) 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 (…...

基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容
基于 UniApp + WebSocket实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...
基于matlab策略迭代和值迭代法的动态规划
经典的基于策略迭代和值迭代法的动态规划matlab代码,实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...

MFC 抛体运动模拟:常见问题解决与界面美化
在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...

宇树科技,改名了!
提到国内具身智能和机器人领域的代表企业,那宇树科技(Unitree)必须名列其榜。 最近,宇树科技的一项新变动消息在业界引发了不少关注和讨论,即: 宇树向其合作伙伴发布了一封公司名称变更函称,因…...
tomcat指定使用的jdk版本
说明 有时候需要对tomcat配置指定的jdk版本号,此时,我们可以通过以下方式进行配置 设置方式 找到tomcat的bin目录中的setclasspath.bat。如果是linux系统则是setclasspath.sh set JAVA_HOMEC:\Program Files\Java\jdk8 set JRE_HOMEC:\Program Files…...

论文阅读:Matting by Generation
今天介绍一篇关于 matting 抠图的文章,抠图也算是计算机视觉里面非常经典的一个任务了。从早期的经典算法到如今的深度学习算法,已经有很多的工作和这个任务相关。这两年 diffusion 模型很火,大家又开始用 diffusion 模型做各种 CV 任务了&am…...
ThreadLocal 源码
ThreadLocal 源码 此类提供线程局部变量。这些变量不同于它们的普通对应物,因为每个访问一个线程局部变量的线程(通过其 get 或 set 方法)都有自己独立初始化的变量副本。ThreadLocal 实例通常是类中的私有静态字段,这些类希望将…...
Django RBAC项目后端实战 - 03 DRF权限控制实现
项目背景 在上一篇文章中,我们完成了JWT认证系统的集成。本篇文章将实现基于Redis的RBAC权限控制系统,为系统提供细粒度的权限控制。 开发目标 实现基于Redis的权限缓存机制开发DRF权限控制类实现权限管理API配置权限白名单 前置配置 在开始开发权限…...

C++中vector类型的介绍和使用
文章目录 一、vector 类型的简介1.1 基本介绍1.2 常见用法示例1.3 常见成员函数简表 二、vector 数据的插入2.1 push_back() —— 在尾部插入一个元素2.2 emplace_back() —— 在尾部“就地”构造对象2.3 insert() —— 在任意位置插入一个或多个元素2.4 emplace() —— 在任意…...