indexDB讲解与封装

indexedDB,非关系型数据库,W3C标准推荐
indexedDB是一种轻量级NOSQL数据库,是由浏览器自带。相比Web Sql更加高效,包括索引、事务处理和查询功能。
在HTML5本地存储中,IndexedDB存储的数据是最多的,不像webStorage的4M,IndexedDB存储空间是无上限且永久的。
indexedDB支持度情况:
http://caniuse.com/#search=indexdb
对于ios10以前的版本有下面这一条说明:
Partial support in Safari & iOS 8 & 9 refers to seriously buggy behavior as well as complete lack of support in WebViews.
部分支持Safari和iOS 8和9指的是严重的错误行为以及在WebViews完全缺乏支持。
Ok,如果,我们要用indexedDB,对于ios10之前的版本,那到底有多严重呢?
这个api里面有详细的介绍,其实基本的一些操作是可以用的。
https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API
在讨论过indexedDB是否可用后,我们来正式学习一下它:
indexedDB特点:

  1. 键值对存储:IndexedDB内部采用对象仓库(object store)存放数据。所有类型的数据都可以直接存入,包括JavaScript对象。在对象仓库中,数据以“键值对”的形式保存,每一个数据都有对应的键名,键名是独一无二的,不能有重复,否则会抛出一个错误。
  2. 异步:IndexedDB操作时不会锁死浏览器,用户依然可以进行其他操作,这与localStorage形成对比,后者的操作是同步的。异步设计是为了防止大量数据的读写,拖慢网页的表现。
  3. 支持事务:IndexedDB支持事务(transaction),这意味着一系列操作步骤之中,只要有一步失败,整个事务就都取消,数据库回到事务发生之前的状态,不存在只改写一部分数据的情况。
  4. 同域限制 :您也受到同域限制,每一个数据库对应创建该数据库的域名。来自不同域名的网页,只能访问自身域名下的数据库,而不能访问其他域名下的数据库。
  5. 储存空间大:IndexedDB的储存空间比localStorage大得多,一般来说不少于250MB。IE的储存上限是250MB,Chrome和Opera是剩余空间的某个百分比,Firefox则没有上限。
  6. 支持二进制储存: IndexedDB不仅可以储存字符串,还可以储存二进制数据。
indexedDB的主要方法:
1.创建数据库window.indexedDB.open() 我们可以这样创建或打开一个本地的数据库对象
var openRequest =window.indexedDB.open(name, version); var db; openRequest.onupgradeneeded = function(e) { console.log("Upgrading..."); } openRequest.onsuccess = function(e) { console.log("Success!"); db = e.target.result; } openRequest.onerror = function(e) { console.log("Error"); console.dir(e); }

(1)第一次打开数据库时,会先触发upgradeneeded事件,然后触发success事件。
(2)open方法返回的是一个对象(IDBOpenDBRequest),回调函数定义在这个对象上面。
(3)回调函数接受一个事件对象event作为参数,它的target.result属性就指向打开的IndexedDB数据库。
如图为chrom浏览器中的打开后的对象
indexDB讲解与封装
文章图片
image.jpeg 2.indexedDB实例对象的方法—createObjectStore createObjectStore方法用于创建存放数据的“对象仓库”(object store),类似于传统关系型数据库的表格。
db.createObjectStore("test", { keyPath: "email" }); db.createObjectStore("test2", { autoIncrement: true });

上面代码中的keyPath属性表示,所存入对象的email属性用作每条记录的键名(由于键名不能重复,所以存入之前必须保证数据的email属性值都是不一样的),默认值为null;autoIncrement属性表示,是否使用自动递增的整数作为键名(第一个数据为1,第二个数据为2,以此类推),默认为false。一般来说,keyPath和autoIncrement属性只要使用一个就够了,如果两个同时使用,表示键名为递增的整数,且对象不得缺少指定属性。
if(!db.objectStoreNames.contains("firstOS")) { db.createObjectStore(“firstOS”); }

上面代码先判断某个“对象仓库”是否存在,如果不存在就创建该对象仓库。
3.indexedDB实例对象的方法—transaction transaction方法用于创建一个数据库事务。向数据库添加数据之前,必须先创建数据库务。
transaction方法返回一个事务对象,该对象的objectStore方法用于获取指定的对象仓库。
var transaction = db.transaction(["firstOS"],"readwrite"); var store = transaction.objectStore("firstOS");

transaction方法接受两个参数:
第一个参数是一个数组,里面是所涉及的对象仓库,通常是只有一个;
第二个参数是一个表示操作类型的字符串。readonly(只读)和readwrite(读写)。
transaction方法有三个事件,可以用来定义回调函数。
abort: 事务中断; complete: 事务完成; error: 事务出错。
transaction.oncomplete = function(event) { // some code };

4.transaction对象—添加数据: add方法 【indexDB讲解与封装】获取对象仓库以后,就可以用add方法往里面添加数据了。
var transaction = db.transaction(["firstOS"],"readwrite"); var store = transaction.objectStore(“firstOS”); var o = {p: 123}; var request = store.add(o,1); request.onerror = function(e) { console.log("Error",[e.target.error.name](http://e.target.error.name)); } request.onsuccess = function(e) { console.log("数据添加成功!"); }

add方法的第一个参数是所要添加的数据,第二个参数是这条数据对应的键名(key),上面代码将对象o的键名设为1。如果在创建数据仓库时,对键名做了设置,这里也可以不指定键名。上述操作也可以写成链式的
下图为一个学生对象添加到数据库中在chrom浏览器中的存储情况:
indexDB讲解与封装
文章图片
image.jpeg indexDB讲解与封装
文章图片
image.jpeg 5.transaction对象—其他方法 (1)更新记录:put方法,更新数据仓库中的对象
var o = { p:456 }; var request = store.put(o);

(2)读取数据:get方法,通过键值key获取数据仓库中的对象
var request = store.get(key);

(3)删除记录:delete方法,通过键值key删除数据仓库中的对象
var request = store.delete(key);

(4)清空数据仓库:clear方法,删除整个object store中的数据
var request = store.clear();

(5)遍历数据:openCursor方法,利用游标遍历数据,下面详细讲一下这个方法
var request = store.openCursor();

6.transaction对象—遍历数据:openCursor方法 (1)利用游标遍历数据
var request = store.openCursor(); request.onsuccess = function(e) { var cursor = e.target.result; if(cursor && cursor !== null) { console.log("Key", cursor.key); console.dir("Data", cursor.value); cursor.continue(); }else{ console.log(“遍历结束"); } request.onerror = function(e) { console.log("Error",[e.target.error.name](http://e.target.error.name)); }

(2)利用游标 返回 或 删除 指定的数据
var request = store.openCursor(); var data = https://www.it610.com/article/[]; request.onsuccess = function(e) { var cursor = e.target.result; if(cursor && cursor !== null) { if(cursor.[value.name](http://value.name) === ‘jack’){ data.push(cursor.value); // 或者在此删除数据 // cursor.delete(); } cursor.continue(); }else{ console.log(“遍历结束”); // 在此resolve(data); } request.onerror = function(e) { console.log("Error",[e.target.error.name](http://e.target.error.name)); }

7.indexedDB实例对象的方法—索引createIndex与index 如图一个学生对象,创建索引:
indexDB讲解与封装
文章图片
image.jpeg (1)createIndex方法用于创建索引。
var store = db.createObjectStore('student',{keyPath: “id”}); store.createIndex(‘emailIndex’,'email',{unique:true}); //邮箱store.createIndex(‘classIndex’,'class',{unique:false}); //班级store.createIndex(‘sexIndex','sex',{unique:false}); //性别store.createIndex(‘classSexIndex’,['class','sex'],{unique:false}); //班级+性别

(2)createIndex方法接受三个参数,第一个是索引名称,第二个是建立索引的属性名,第三个是参数对象,用来设置索引特性,unique表示索引所在的属性是否有唯一值。
var index = store.index(“emailIndex"); index.get('jack').onsuccess=function(e){ var student=e.target.result; console.log(student.id); }

Index.get用来获取唯一索引,如果不是唯一的,就要用到游标
8.IDBKeyRange对象—指定游标范围 索引的有用之处,还在于可以指定读取数据的范围。这需要用到浏览器原生的IDBKeyRange对象。
IDBKeyRange对象的作用是生成一个表示范围的Range对象。生成方法有四种:
lowerBound方法:指定范围的下限。
upperBound方法:指定范围的上限。
bound方法:指定范围的上下限。
only方法:指定范围中只有一个值。
var r1 = IDBKeyRange.upperBound(x); All keys ≤ xvar r2 = IDBKeyRange.upperBound(x, true); All keys < xvar r3 = IDBKeyRange.lowerBound(y); All keys ≥ yvar r4 = IDBKeyRange.lowerBound(y, true); All keys > yvar r5 = IDBKeyRange.bound(x, y); All keys ≥ x && ≤ yvar r6 = IDBKeyRange.bound(x, y, true, true); All keys > x &&< yvar r7 = IDBKeyRange.bound(x, y, true, false); All keys > x && ≤ yvar r8 = IDBKeyRange.bound(x, y, false, true); All keys ≥ x &&< yvar r9 = IDBKeyRange.only(z); // The key = z

如下图所示的方法,便可
利用索引查询与特定条件匹配的所有记录
利用sexIndex查找所有男同学的信息
getIndex(‘student’,’sexIndex’,’male’);

利用classSexIndex查找所有二年六班的女同学的信息
getIndex(‘student’,’classSexIndex’,[‘二年六班’,’female’]);

由此可见,在项目中使用,对于数据库的封装饰很有必要的,
indexDB讲解与封装
文章图片
image.jpeg 下面我在项目中封装的indexedDB的一些部分,仅供参考:
dbModule.factory('iDbService',["$http", "$q", function ($http,$q) { var myDB = { name : 'localIndexDB', version : 1, db : null }; var openDB = function (name, version, stores) { console.log('openDB'); var d = $q.defer(); var _name = [myDB.name](http://myDB.name) || name; var _version = myDB.version || version; //打开数据库 var result = window.indexedDB.open(_name,_version); //错误 result.onerror = function(e){ console.log("Open DB Error!"); d.reject("error"); }; //正确打开 result.onsuccess = function(e){ myDB.db = e.target.result ; console.log('success'); d.resolve("success"); }; //数据库版本变更 result.onupgradeneeded = function(e){ myDB.db = e.target.result ; if(!myDB.db.objectStoreNames.contains('users')){ myDB.db.createObjectStore('users',{keyPath: "id"}); } if(!myDB.db.objectStoreNames.contains('infos')){ myDB.db.createObjectStore('infos',{autoIncrement: true}); } if(!myDB.db.objectStoreNames.contains('problem')){ var store = myDB.db.createObjectStore('problem',{keyPath: "fqid"}); // 建数据仓库object store store.createIndex('minTaskIndex',['fmid','fstate'],{unique:false}); // 创建索引 } console.log('upgradeneeded'); d.resolve("upgradeneeded"); }; return d.promise; }; openDB(); // 通过key查询数据 var get = function (storeName,key) { var d = $q.defer(); //promise var _db = myDB.db; var transaction = _db.transaction(storeName,'readonly'); var store = transaction.objectStore(storeName); var result = store.get(key); result.onsuccess = function (e) { var data = https://www.it610.com/article/e.target.result; console.log('indexDb 一条数据查询结果为:'); console.log(data); d.resolve(data); }; result.onerror = function (e) { d.reject(); }; return d.promise; }; // 查询一个object store的所有数据 var getAll = function(storeName){ var d = $q.defer(); //promise var _db = myDB.db; var transaction = _db.transaction(storeName,'readonly'); var store = transaction.objectStore(storeName); var result = store.openCursor(); //打开一个游标 var data = https://www.it610.com/article/[]; result.onsuccess = function (e) { var cursor = e.target.result; if (cursor && cursor !== null) { var problem = cursor.value; var jsonStr = JSON.stringify(problem); data.push(problem); cursor.continue(); }else { console.log('indexDb 一张表数据查询结果为:'); // console.log(data); d.resolve(data); } }; result.onerror = function (e) { d.reject(); }; return d.promise; }; // 根据索引,查询与特定条件匹配的所有记录 var getIndex = function(storeName,indexName,params){ var d = $q.defer(); //promise console.log('索引查询的params'); console.log(params); var _db = myDB.db; var transaction = _db.transaction(storeName,'readonly'); var store = transaction.objectStore(storeName); var index = store.index(indexName); var range = IDBKeyRange.only(params); var result = index.openCursor(range); //打开一个游标 var data = https://www.it610.com/article/[]; result.onsuccess = function (e) { var cursor = e.target.result; if (cursor && cursor !== null) { var problem = cursor.value; var jsonStr = JSON.stringify(problem); data.push(problem); cursor.continue(); }else { console.log('indexDb' + storeName + '表中' + indexName + '索引,数据查询结果为:'); // console.log(data); d.resolve(data); }; result.onerror = function (e) { d.reject(); }; return d.promise; }; // 新增数据 var add = function (storeName,value) { var d = $q.defer(); var _db = myDB.db; var transaction = _db.transaction(storeName,'readwrite'); var store = transaction.objectStore(storeName); var result = store.add(value); result.onsuccess = function(e) { console.log("create note success!"); console.log(myDB); d.resolve(); }; result.onerror = function(e) { console.log("can't create database,error:" + result.error); d.reject(); }; return d.promise; }; // 插入更新数据 var put = function (storeName,value) { var d = $q.defer(); var _db = myDB.db; var transaction = _db.transaction(storeName,'readwrite'); var store = transaction.objectStore(storeName); var result = store.put(value); result.onsuccess = function(e) { console.log("create note success!"); d.resolve(); }; result.onerror = function(e) { console.log("can't create database,error:" + result.error); d.reject(); }; return d.promise; }; // 通过key删除对象 var remove = function (storeName,key) { var _db = myDB.db; var transaction = _db.transaction(storeName,'readwrite'); var store = transaction.objectStore(storeName); var result = store.delete(key); result.onsuccess = function (e) { console.log(e); }; result.onerror = function (e) { console.log(e); }; }; // clear方法可以清空object store var clear = function (storeName) { var _db = myDB.db; var transaction = _db.transaction(storeName,'readwrite'); var store = transaction.objectStore(storeName); var result = store.clear(); result.onsuccess = function (e) { console.log(e); }; result.onerror = function (e) { console.log(e); }; }; // 删除与特定条件匹配的所有记录 var removeData = https://www.it610.com/article/function(storeName,arg,value){ var d = $q.defer(); //promise console.log('删除匹配值的arg+value'); console.log(arg); console.log(value); var _db = myDB.db; var transaction = _db.transaction(storeName,'readwrite'); var store = transaction.objectStore(storeName); var result = store.openCursor(); //打开一个游标 result.onsuccess = function (e) { var cursor = e.target.result; if (cursor && cursor !== null) { if(cursor.value[arg[0]] === value[0] && cursor.value[arg[1]] === value[1]) { var request = cursor.delete(); request.onsuccess = function() { console.log('Deleted this cursor data.'); }; } else { console.log('131231312'); } cursor.continue(); }else { console.log('Entries displayed.'); d.resolve(); } }; result.onerror = function (e) { d.reject(); }; return d.promise; }; return { openDB: openDB, get: get, getAll: getAll, getIndex: getIndex, add: add, put: put, remove: remove, clear: clear, removeData: removeData }; }]);

    推荐阅读