导语
在HTML5强势来袭时,前端存储数据的方案变的很多,有Web Storage(Local Storage、Session Storage)、Cookie、Web SQL Database、IndexedDB。
近些年Web Storage、Cookie已经用烂了,Web SQL Database也已经被废弃,唯独IndexedDB提起的很少,今天就探究一下IndexedDB使用的方式。
下面是使用 React + Webpack + ES6 完成的IndexedDB管理平台
https://zhensherlock.github.io/react-indexeddb-system/
IndexedDB
IndexedDB是一种可以让开发者将数据保存在客户端中,保存的数据可以被查询,并且可以离线使用。IndexedDB对于那些需要存储大量数据,或者是需要离线使用的程序是非常有效的解决方法。
下面是IndexedDB数据库的特点:
非关系型数据库
作为WebSQL数据库的取代品,IndexedDB和WebSQL的不同点在于WebSQL是关系型数据库(比如:SQL Server,MySQL)IndexedDB是非关系型数据库(比如:mongodb)。
IndexedDB不使用SQL来操作数据库。 它属于noSQL数据库。
IndexedDB数据库使用key-value键值对储存数据,values数据可以是各式各样的结构体,也可以是文件、blobs等数据类型。
事务模式
IndexedDB的任何数据库操作行为都必须要发生在事务中,如果在事务外执行数据库操作,都会提示报错。IndexedDB事务是自动提交,而非手动提交。
事务主要用在高并发系统中,当用户在不同的标签页同时打开多个实例时,这时如果没有事务功能,这些实例就会互相影响,导致数据错乱。
异步同步
IndexedDB分别提供了同步和异步访问的API,同步API目前还没有被任何浏览器所实现,并且同步API只能在Web Workers中使用。异步API在Web Workers内部和外部都可以使用。
异步API方法调用完后会继续向后执行,而不会阻塞调用线程,如果想要返回数据需要开发者提供一个回调函数来接受数据,当操作完成时,数据库会以DOM事件的方式通知你,同时事件的类型会告诉你这个操作是否成功完成。
这个和XMLHttpRequest请求是类似的。
同源(same-origin)策略
IndexedDB遵循同源(same-origin)策略,同源下,数据共享。所以你只能访问同源中存储的数据,而不能访问其他源的。域名、应用层协议和端口只要有一个不同,就是不同源。
数据库的CURD
下面来看一下IndexedDB操作示例,以下示例使用了部分es6语法:
打开数据库
var request = window.indexedDB.open('library', 1); var dbObject = {}; request.onerror = event => { alert("不能打开数据库,错误代码: " + event.target.errorCode); }; request.onsuccess = event => { dbObject.db = event.target.result; }; request.onupgradeneeded = event => { ... }
|
操作数据库结构
request.onupgradeneeded = event => { dbObject.db = event.target.result; if(!dbObject.db.objectStoreNames.contains('books')) { var store = dbObject.db.createObjectStore("books", {keyPath: "isbn"}); store.createIndex("by_title", "title", {unique: true}); store.createIndex("by_author", "author"); } if(!dbObject.db.objectStoreNames.contains('user')) { var userStore = dbObject.db.createObjectStore("user", { autoIncrement:true }); userStore.createIndex("by_name", "name", {unique: true}); } }
|
新增和修改数据
let transaction = dbObject.db.transaction(['books'], "readwrite"); let store = transaction.objectStore('books'); let request = store.put({title: title, author: author, isbn: isbn}); request.onsuccess = () => { console.log('put complete'); }; request.onerror = (event) => { console.log(event.target.errorCode); };
|
如果只想添加可以使用add方法,当主键值相冲突时,会执行操作失败的回调函数。
let request = store.add({title: title, author: author, isbn: isbn});
|
删除数据
dbObject.db.transaction('books', 'readwrite').objectStore('books').delete(isbn); dbObject.db.transaction('books', 'readwrite').objectStore('books').clear();
|
以上方法执行的结果都必须使用onsuccess和onerror中获取。
读取数据
使用索引来查询数据的前提是你只需要一条数据,如果有多条数据满足情况,你总是得到键值最小的那个。
let store = dbObject.db.transaction('books').objectStore('books'); store.index('by_title').get(title).onsuccess = (event) => { console.log(event.target.result); };
|
当我们筛选数据时需要返回多条数据的时候,我们就需要使用游标来实现。
let store = dbObject.db.transaction('books').objectStore('books'); store.index('by_author').openCursor(IDBKeyRange.only(author)).onsuccess = (event) => { let cursor = event.target.result; if(cursor){ let obj = cursor.value; console.log(obj); cursor.continue(); } };
|
上面用到的IDBKeyRange是用于添加查询条件的,相当于sql中的where从句,IDBKeyRange有以下几个方法:
1、只匹配条件为 “michael” 值
IDBKeyRange.only('michael')
|
2、缩小匹配范围,从 “michael” 到底部,包括 “michael”
IDBKeyRange.lowerBound('michael');
|
3、缩小匹配范围,从 “michael” 到底部,不包括 “michael”
IDBKeyRange.lowerBound('michael', true);
|
4、缩小匹配范围,从头到 “michael” ,包括 “michael”
IDBKeyRange.upperBound('michael');
|
5、缩小匹配范围,从头到 “michael” ,不包括 “michael”
IDBKeyRange.upperBound('michael', true);
|
6、缩小匹配范围,从头到 “michael” ,不包括 “michael”
IDBKeyRange.upperBound('michael', true);
|
7、缩小匹配范围,从 “michael” 到 “john” ,第三个参数是否不包括 “michael” ,第四个参数是否不包括 “john”
IDBKeyRange.bound('michael', 'john', true, true);
|
参考文献
IndexedDB
基本概念
使用 IndexedDB
Indexed Database API 2.0
HTML5本地存储——IndexedDB(一:基本使用)
HTML5本地存储——IndexedDB(二:索引)
HTML5本地存储IndexedDB基础介绍(一)-数据库的简单增删改查
HTML5本地存储IndexedDB基础介绍(二)- 游标和索引
indexedDB如何实现满足多重条件查询?