IndexedDB详解
文章目录
- 简介
- IndexedDB简介
- IndexedDB的使用
- IndexedDB的浏览器支持
- 创建IndexedDB
- indexdb中的CURD
- 使用游标cursor
简介
IndexedDB是一种在浏览器端存储数据的方式。既然称之为DB,是因为它丰富了客户端的查询方式,并且因为是本地存储,可以有效的减少网络对页面数据的影响。
有了IndexedDB,浏览器可以存储更多的数据,从而丰富了浏览器端的应用类型。
IndexedDB简介
IndexedDB和传统的关系型数据不同的是,它是一个key-value型的数据库。
value可以是复杂的结构体对象,key可以是对象的某些属性值也可以是其他的对象(包括二进制对象)。你可以使用对象中的任何属性做为index,以加快查找。
IndexedDB是自带transaction的,所有的数据库操作都会绑定到特定的事务上,并且这些事务是自动提交了,IndexedDB并不支持手动提交事务。
IndexedDB API大部分都是异步的,在使用异步方法的时候,API不会立马返回要查询的数据,而是返回一个callback。
异步API的本质是向数据库发送一个操作请求,当操作完成的时候,会收到一个DOM event,通过该event,我们会知道操作是否成功,并且获得操作的结果。
IndexedDB是一种 NoSQL 数据库,和关系型数据库不同的是,IndexedDB是面向对象的,它存储的是Javascript对象。
IndexedDB还有一个很重要的特点是其同源策略,每个源都会关联到不同的数据库集合,不同源是不允许访问其他源的数据库,从而保证了IndexedDB的安全性。
IndexedDB的使用
这一节,我们将会以具体的例子来讲解如何使用IndexedDB。
IndexedDB的浏览器支持
不同的浏览器对于IndexedDB有不同的实现,正常来说,我们可以使用window.indexedDB来获取到浏览器的indexedDB对象。但是对于某些浏览器来说,还没有使用标准的window.indexedDB,而是用带前缀的实现。
所以我们在使用过程中通常需要进行判断和转换:
// In the following line, you should include the prefixes of implementations you want to test.
window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
// DON'T use "var indexedDB = ..." if you're not in a function.
// Moreover, you may need references to some window.IDB* objects:
window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction || {READ_WRITE: "readwrite"}; // This line should only be needed if it is needed to support the object's constants for older browsers
window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;
// (Mozilla has never prefixed these objects, so we don't need window.mozIDB*)
上面我们从window获取了indexedDB,IDBTransaction和IDBKeyRange三个对象。
其中indexedDB表示的是数据库的连接。IDBTransaction表示的是transaction,而IDBKeyRange则是用从数据库的某个特定key range中取出数据。
但是,通常来说带前缀的实现一般都是不稳定的,所以我们通常不建议在正式环境中使用,所以如果不支持标准表达式的话,需要直接报错:
if (!window.indexedDB) {console.log("Your browser doesn't support a stable version of IndexedDB. Such and such feature will not be available.");
}
创建IndexedDB
要使用IndexedDB,我们首先需要open it:
// Let us open our database
var request = window.indexedDB.open("MyTestDatabase", 3);
open方法返回一个IDBOpenDBRequest对象,同时这是一个异步操作,open操作并不会立马打开数据库或者开启事务,我们可以通过监听request的事件来进行相应的处理。
open方法传入两个参数,第一个参数是数据库的名字,第二个参数是数据库的版本号。
当你创建一个新的数据库或者升级一个现有的数据库版本的时候,将会触发一个onupgradeneeded事件,并在事件中传入IDBVersionChangeEvent,我们可以通过event.target.result来获取到IDBDatabase对象,然后通过这个对象来进行数据库的版本升级操作。如下所示:
// This event is only implemented in recent browsers
request.onupgradeneeded = function(event) { // Save the IDBDatabase interface var db = event.target.result;// Create an objectStore for this databasevar objectStore = db.createObjectStore("name", { keyPath: "myKey" });
};
注意,这里的版本号是一个整数。如果你传入一个float,那么将会对该float进行取整操作。
有了request,我们可以通过监听onerror或者onsuccess事件来进行相应的处理。
var db;
var request = indexedDB.open("MyTestDatabase");
request.onerror = function(event) {console.log("Why didn't you allow my web app to use IndexedDB?!");
};
request.onsuccess = function(event) {db = event.target.result;
};
拿到db对象之后,我们可以设置全局的异常处理:
db.onerror = function(event) {// Generic error handler for all errors targeted at this database's// requests!console.error("Database error: " + event.target.errorCode);
};
IndexedDB中的table叫做object stores,和关系型数据库中的table一样,object stores中的每一个对象都和一个key相关联,和key相关的有两个概念 key path 和 key generator.
如果存储的是javascript Object对象,那么可以指定该对象中的某一个属性作为key path,那么这个属性将会被作为key。
如果没有指定key path,那么存储的Object可以是任何对象,甚至是基础类型比如数字和String。
而key generator就是key的生成器。
假如我们想要存储这样的数据:
// This is what our customer data looks like.
const customerData = [{ ssn: "444-44-4444", name: "Bill", age: 35, email: "bill@company.com" },{ ssn: "555-55-5555", name: "Donna", age: 32, email: "donna@home.org" }
];
看一下对应的数据库操作是怎么样的:
const dbName = "the_name";var request = indexedDB.open(dbName, 2);request.onerror = function(event) {// Handle errors.
};
request.onupgradeneeded = function(event) {var db = event.target.result;// Create an objectStore to hold information about our customers. We're// going to use "ssn" as our key path because it's guaranteed to be// unique - or at least that's what I was told during the kickoff meeting.var objectStore = db.createObjectStore("customers", { keyPath: "ssn" });// Create an index to search customers by name. We may have duplicates// so we can't use a unique index.objectStore.createIndex("name", "name", { unique: false });// Create an index to search customers by email. We want to ensure that// no two customers have the same email, so use a unique index.objectStore.createIndex("email", "email", { unique: true });// Use transaction oncomplete to make sure the objectStore creation is // finished before adding data into it.objectStore.transaction.oncomplete = function(event) {// Store values in the newly created objectStore.var customerObjectStore = db.transaction("customers", "readwrite").objectStore("customers");customerData.forEach(function(customer) {customerObjectStore.add(customer);});};
};
我们需要在onupgradeneeded事件中处理所有的schema相关的操作。
首先使用db.createObjectStore创建了一个customers的ObjectStore,并且使用了对象的keypath作为key。
除了key之外,我们创建了两个index,以提高查询速度。
最后我们监听transaction.oncomplete事件,并在里面加入存储object的操作。
上面的代码中,我们使用了keyPath作为key。
下面是一个使用key Generator的例子:
var objStore = db.createObjectStore("names", { autoIncrement : true });
indexdb中的CURD
indexedDB的所有操作都需要在事务中,我们看一个开启事务的操作:
var transaction = db.transaction(["customers"], "readwrite");
上面的例子中使用readwrite来操作customers ObjectStore。
transaction接收两个参数,第一个参数是一个数组,数组中是这个trans中将会处理的ObjectStores,第二个参数是处理的模式。
有了transaction之后,我们可以监听事务的complete和error操作,然后就可以进行add操作了:
// Do something when all the data is added to the database.
transaction.oncomplete = function(event) {console.log("All done!");
};transaction.onerror = function(event) {// Don't forget to handle errors!
};var objectStore = transaction.objectStore("customers");
customerData.forEach(function(customer) {var request = objectStore.add(customer);request.onsuccess = function(event) {// event.target.result === customer.ssn;};
});
上面的例子中,我们使用了add方法,add的前提是数据库中并不存在相同key的对象。除了add方法之外,我们还可以使用put方法,put方法主要用来进行更新操作。
再看一个删除的操作:
var request = db.transaction(["customers"], "readwrite").objectStore("customers").delete("444-44-4444");
request.onsuccess = function(event) {// It's gone!
};
现在我们的数据库已经有了数据,我们看下怎么进行查询:
var transaction = db.transaction(["customers"]);
var objectStore = transaction.objectStore("customers");
var request = objectStore.get("444-44-4444");
request.onerror = function(event) {// Handle errors!
};
request.onsuccess = function(event) {// Do something with the request.result!console.log("Name for SSN 444-44-4444 is " + request.result.name);
这里,我们直接使用了db.transaction,默认情况下是readonly模式的。
下面是一个更新的例子:
var objectStore = db.transaction(["customers"], "readwrite").objectStore("customers");
var request = objectStore.get("444-44-4444");
request.onerror = function(event) {// Handle errors!
};
request.onsuccess = function(event) {// Get the old value that we want to updatevar data = event.target.result;// update the value(s) in the object that you want to changedata.age = 42;// Put this updated object back into the database.var requestUpdate = objectStore.put(data);requestUpdate.onerror = function(event) {// Do something with the error};requestUpdate.onsuccess = function(event) {// Success - the data is updated!};
};
更新我们使用的是put方法。
使用游标cursor
indexedDB支持游标操作,我们可以使用cursor来遍历objectStore的数据:
var objectStore = db.transaction("customers").objectStore("customers");objectStore.openCursor().onsuccess = function(event) {var cursor = event.target.result;if (cursor) {console.log("Name for SSN " + cursor.key + " is " + cursor.value.name);cursor.continue();}else {console.log("No more entries!");}
};
openCursor可以接受多个参数,第一个参数可以接受key的查询范围,第二个参数用来指定遍历的方向。如果两个参数都为空的话,默认是所有的数据的以升序的顺序遍历。
如果想遍历下一个游标,则可以调用cursor.continue。
我们看一下两个参数的游标使用:
// Only match "Donna"
var singleKeyRange = IDBKeyRange.only("Donna");// Match anything past "Bill", including "Bill"
var lowerBoundKeyRange = IDBKeyRange.lowerBound("Bill");// Match anything past "Bill", but don't include "Bill"
var lowerBoundOpenKeyRange = IDBKeyRange.lowerBound("Bill", true);// Match anything up to, but not including, "Donna"
var upperBoundOpenKeyRange = IDBKeyRange.upperBound("Donna", true);// Match anything between "Bill" and "Donna", but not including "Donna"
var boundKeyRange = IDBKeyRange.bound("Bill", "Donna", false, true);// To use one of the key ranges, pass it in as the first argument of openCursor()/openKeyCursor()
index.openCursor(boundKeyRange, "prev").onsuccess = function(event) {var cursor = event.target.result;if (cursor) {// Do something with the matches.cursor.continue();}
};
除了openCursor,我们还可以通过使用openKeyCursor来遍历KeyCursor:
// Using a normal cursor to grab whole customer record objects
index.openCursor().onsuccess = function(event) {var cursor = event.target.result;if (cursor) {// cursor.key is a name, like "Bill", and cursor.value is the whole object.console.log("Name: " + cursor.key + ", SSN: " + cursor.value.ssn + ", email: " + cursor.value.email);cursor.continue();}
};// Using a key cursor to grab customer record object keys
index.openKeyCursor().onsuccess = function(event) {var cursor = event.target.result;if (cursor) {// cursor.key is a name, like "Bill", and cursor.value is the SSN.// No way to directly get the rest of the stored object.console.log("Name: " + cursor.key + ", SSN: " + cursor.primaryKey);cursor.continue();}
};
除此之外,我们还可以直接通过index来进行查询:
var index = objectStore.index("name");index.get("Donna").onsuccess = function(event) {console.log("Donna's SSN is " + event.target.result.ssn);
};
要使用index的前提就是需要在request.onupgradeneeded中创建index。
本文作者:flydean程序那些事
本文链接:http://www.flydean.com/indexeddb-kickoff/
本文来源:flydean的博客
欢迎关注我的公众号:「程序那些事」最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!
IndexedDB详解相关推荐
- web存储详解(cookie、sessionStorage、localStorage、indexedDB)
目录 一.web存储概念简介 1. 什么是web存储? 2. 为什么需要web存储? 二.web存储详解 1. cookie 2. sessionStorage和localStorage (1). 相 ...
- Sentry For Vue 完整接入详解(2021 Sentry v21.8.x)前方高能预警!三万字,慎入!
内容源于:https://docs.sentry.io/platforms/javascript/guides/vue/ 系列 1 分钟快速使用 Docker 上手最新版 Sentry-CLI - 创 ...
- 【面试题】详解Cookie、localStorage、sessionStorage区别
[面试题]详解Cookie.localStorage.sessionStorage区别 三者基本概念 Cookie localStorage sessionStorage 安全性的考虑 Cookie. ...
- 跨域详解!前后端分离解决跨域问题
文章目录 一.为什么会出现跨域问题 二.什么是跨域 三.非同源限制 四.跨域问题的解决方式 Jsonp前后端配合 前端修改 后端修改 CORS 详解响应头 5. SpringBoot解决 [方式一]全 ...
- 详解Javascript本地存储的方式、区别及应用场景
详解Javascript本地存储的方式.区别及应用场景 一.方式 javaScript本地缓存的方法我们主要讲述以下四种: cookie sessionStorage localStorage ind ...
- 从命令行到IDE,版本管理工具Git详解(远程仓库创建+命令行讲解+IDEA集成使用)
首先,Git已经并不只是GitHub,而是所有基于Git的平台,只要在你的电脑上面下载了Git,你就可以通过Git去管理"基于Git的平台"上的代码,常用的平台有GitHub.Gi ...
- JVM年轻代,老年代,永久代详解
秉承不重复造轮子的原则,查看印象笔记分享连接↓↓↓↓ 传送门:JVM年轻代,老年代,永久代详解 速读摘要 最近被问到了这个问题,解释的不是很清晰,有一些概念略微模糊,在此进行整理和记录,分享给大家.在 ...
- docker常用命令详解
docker常用命令详解 本文只记录docker命令在大部分情境下的使用,如果想了解每一个选项的细节,请参考官方文档,这里只作为自己以后的备忘记录下来. 根据自己的理解,总的来说分为以下几种: Doc ...
- 通俗易懂word2vec详解词嵌入-深度学习
https://blog.csdn.net/just_so_so_fnc/article/details/103304995 skip-gram 原理没看完 https://blog.csdn.net ...
最新文章
- 如何用Python和深度神经网络识别图像?
- Science公布2021年度十大科学突破,AI这项前所未有的突破上榜
- hexo+githup搭建属于自己的博客
- javase(Properties集合及学生对象信息录入文本中案例)
- 第六章:系统困境之 你的努力与时代进程相逆
- centos利用nexus搭建局域网docker私有仓库
- 【英语学习】【Level 07】U06 First Time L1 My very first trip
- MySQL三种打开方式
- PostgreSQL 9.6 同步多副本 与 remote_apply事务同步级别 应用场景分析
- 【渝粤教育】国家开放大学2018年春季 0314-21T兽医基础 参考试题
- 前缀无歧义编码(PFC)
- R语言画好看的聚类树
- 网银爬虫系统(爬取网银流水,爬取网银余额)难点分析
- 为什么学习嵌入式会搞单片机以及如何学习C51单片机
- storm风暴英雄 tempo_风暴英雄Tempo Storm 安娜版本天梯环境速报 安娜强度居T3
- BeatSaber节奏光剑插件开发官方教程2-简单的插件示例
- java七行情书_七行情书
- 【网络攻击手段之----- DDOS攻击】
- 百度编辑器 百度编辑器
- 【身份证识别】基于matlab GUI BP神经网络身份证识别【含Matlab源码 2239期】
热门文章
- 算法的数值稳定性实验报告用c语言,数值计算实验教案.doc
- 【C 语言小游戏】手打贪吃蛇1,闭关在家37天“吃透”这份345页PDF
- ISO认证证书上常见的认可标志
- 为什么建议大家使用 Linux 开发!
- 大学生必备的十大网站有哪些?
- Tello无人机版之使用Scratch2和ROS进行机器人图形化编程学习
- 浪潮之巅 “八叛徒”与硅谷
- linux boot分区创建,Linux 更换 Boot分区 磁盘 示例
- Revit二次开发—获取内置参数对应的中文字符串,并导出Excel;举例(BuiltParameterGroup、ParameterType和BuiltInCategory)
- 2022年磺化工艺考试练习题及模拟考试