本文经过对IndexedDB
的使用方法和使用场景进行相关介绍,对常见的问题进行解答。javascript
同时,由于MDN中的相关文档缺少相关逻辑性,因此不容易理解。本文将经过项目中常见的数据存储和操做需求来进行内容组织。java
读者可以经过本文学会在项目中正确的使用IndexedDB
,给应用带来的本地存储能力,而且避免一些常见的问题。git
当咱们进行一些较大的SPA页面开发时,咱们会须要进行一些数据的本地存储。github
当数据量不大时,咱们能够经过SessionStorage或者LocalStorage来进行存储,可是当数据量较大,或符合必定的规范时,咱们可使用数据库来进行数据的存储。web
在浏览器提供的数据库中,共有web sql
和IndexedDB
两种。相较于HTML5已经废弃的web sql
来讲,更推荐你们使用IndexedDB
。sql
下面,咱们经过一张图来了解下,IndexedDB总体的结构。数据库
类比sql
型数据库,IndexedDB
中的DB(数据库)就是sql
中的DB,而Object Store(存储空间)
则是数据表
,Item
则等于表中的一条记录
。segmentfault
如今,咱们将其根据IndexedDB
的结构来对其操做进行介绍,能让你们对这个存储空间有一个初步的了解。咱们主要介绍:浏览器
使用IndexedDB
第一步,就是建立或打开一个数据库。咱们使用window.indexedDB.open(DBName)
这个API来打进行操做。具体示例以下:缓存
const request = window.indexedDB.open('test'); request.onupgradeneeded = function (event) { } request.onsuccess = function(event) { //request === event.target; } request.onerror = function(event) {}
调用此接口时,若是当前数据库不存在,则会建立一个新的数据库。
当数据库创建链接时,会返回一个IDBOpenDBRequest
对象。
在链接创建成功时,会触发onsuccess
事件,其中函数参数event
的target
属性就是request
对象。
而在数据库建立或者版本更新时,会触发onupgradeneeded
事件。
window.indexedDB.open
的第二个参数即为版本号。在不指定的状况下,默认版本号为1。具体示例以下:
const request = window.indexedDB.open('test', 2);
在须要更新数据库的schema(模式)
时,须要更新版本号。此时咱们指定一个高于以前版本的版本号,就会触发onupgradeneeded
事件。相似的,当此数据库不存在时,也会触发此事件而且将版本更新到置顶版本。
咱们须要注意的是,版本号是一个Unsigned long long
数字,这意味着它能够是一个很是大的整数。可是,它不能是一个小数,不然它将会被转为最近的整数,同时有可能致使onUpgradeneeded
事件不触发(bug)。
咱们使用createObjectStore
来建立一个存储空间。同时,使用createIndex
来建立它的索引。具体示例以下:
var request = window.indexedDB.open('test', 1); request.onupgradeneeded = function (event) { var db = event.target.result; var objectStore = db.createObjectStore('table1', {keyPath: 'id', autoIncrement: true}); objectStore.createIndex('name', 'name', {unique: false}); } request.onerror = function (event) { alert("Why didn't you allow my web app to use IndexedDB?!"); };
注:只能在onupgradeneeded
回调函数中建立存储空间,而不能在数据库打开后的success
回调函数中建立。
经过createObjectStore
可以建立一个存储空间。接受两个参数:
customers
。keyPath
值为存储对象的某个属性,这个属性可以在获取存储空间数据的时候当作key值使用。autoIncrement
指定了key
值是否自增(当key值为默认的从1开始到2^53的整数时)。而createIndex
可以给当前的存储空间设置一个索引。它接受三个参数:
unique
的值为true
表示不容许索引值相等。在IndexedDB
中,咱们也可以使用事务来进行数据库的操做。事务有三个模式(常量已经弃用):
readOnly
,只读。readwrite
,读写。versionchange
,数据库版本变化。咱们建立一个事务时,须要从上面选择一种模式,若是不指定的话,则默认为只读模式。具体示例以下:
const transaction = db.transaction(['customers'], 'readwrite');
事务函数transaction
的第一个参数为须要关联的存储空间,第二个可选参数为事务模式。与上面相似,事务成功时也会触发onsuccess
函数,失败时触发onerror
函数。
事务的操做都是原子性的。
当存储空间初始化完成后,咱们能够把数据放入存储空间中。直接调用add
方法就能够将数据放入存储空间内,具体示例以下:
var request = window.indexedDB.open('test', 1); request.onsuccess = function (event) { var db = event.target.result; var transaction = db.transaction(['table1'], 'readwrite'); var objectStore = transaction.objectStore('table1'); var index = objectStore.index('name'); objectStore.add({name: 'a', age: 10}); objectStore.add({name: 'b', age: 20}); }
注:add
方法中的第二个参数key值是指定存储空间中的keyPath
值,若是data
中包含keyPath
值或者此值为自增值,那么能够略去此参数。
当咱们须要从存储空间获取数据时,咱们能够经过如下的方法:
var request = window.indexedDB.open('test', 1); request.onsuccess = function (event) { var db = event.target.result; var transaction = db.transaction(['table1'], 'readwrite'); var objectStore = transaction.objectStore('table1'); var request = objectStore.get(1); request.onsuccess = function (event) { // 对 request.result 作些操做! console.log(request.result); }; request.onerror = function (event) { // 错误处理! }; }
当你须要便利整个存储空间中的数据时,你就须要使用到游标。游标使用方法以下:
var request = window.indexedDB.open('test', 1); request.onsuccess = function (event) { var db = event.target.result; var transaction = db.transaction(['table1'], 'readwrite'); var objectStore = transaction.objectStore('table1'); var request = objectStore.openCursor(); request.onsuccess = function (event) { var cursor = event.target.result; if (cursor) { // 使用Object.assign方法是为了不控制台打印时出错 console.log(Object.assign(cursor.value)); cursor.continue(); } }; request.onerror = function (event) { // 错误处理! }; }
使用游标时有一个须要注意的地方,当游标便利整个存储空间可是并未找到给定条件的值时,仍然会触发onsuccess
函数。
openCursor
和openKeyCursor
有两个参数:
第一个参数,遍历范围,指定游标的访问范围。该范围经过一个IDBKeyRange
参数的方法来获取。
遍历范围参数具体示例以下:
// 匹配值 key === 1 const singleKeyRange = IDBKeyRange.only(1); // 匹配值 key >= 1 const lowerBoundKeyRange = IDBKeyRange.lowerBound(1); // 匹配值 key > 1 const lowerBoundOpenKeyRange = IDBKeyRange.lowerBound(1, true); // 匹配值 key < 2 const upperBoundOpenKeyRange = IDBKeyRange.upperBound(2, true); // 匹配值 key >= 1 && key < 2 const boundKeyRange = IDBKeyRange.bound(1, 2, false, true); index.openCursor(boundKeyRange).onsuccess = function(event) { const cursor = event.target.result; if (cursor) { // Do something with the matches. cursor.continue(); } };
第二个参数,便利顺序,指定游标便利时的顺序和处理相同id(keyPath属性指定字段)重复时的处理方法。改范围经过特定的字符串(IDBCursor
的常量已经弃用)来获取。其中:
next
,从前日后获取全部数据(包括重复数据)prev
,从后往前获取全部数据(包括重复数据)nextunique
,从前日后获取数据(重复数据只取第一条,索引重复即认为重复,下同)prevunique
,从后往前获取数据(重复数据只取第一条)遍历顺序参数具体示例以下:
var request = window.indexedDB.open('test', 1); request.onsuccess = function (event) { var db = event.target.result; var transaction = db.transaction(['table1'], 'readwrite'); var objectStore = transaction.objectStore('table1'); var lowerBoundOpenKeyRange = IDBKeyRange.lowerBound(1, false); var request = objectStore.openCursor(lowerBoundOpenKeyRange, IDBCursor.PREV); request.onsuccess = function (event) { var cursor = event.target.result; if (cursor) { // 使用Object.assign方法是为了不控制台打印时出错 console.log(Object.assign(cursor.value)); cursor.continue(); } }; request.onerror = function (event) { // 错误处理! }; }
在前面构建数据库时,咱们建立了两个索引。如今咱们也能够经过索引来进行数据检索。他的本质仍是经过以前获取数据的API来进行,只是将原来使用的keyPath
属性转换成为了索引指定的属性。具体示例以下:
var request = window.indexedDB.open('test', 1); request.onsuccess = function (event) { var db = event.target.result; var transaction = db.transaction(['table1'], 'readwrite'); var objectStore = transaction.objectStore('table1'); var index = objectStore.index('name'); // 第一种,get方法 index.get('a').onsuccess = function (event) { console.log(event.target.result); } // 第二种,普通游标方法 index.openCursor().onsuccess = function (event) { console.log('openCursor:', event.target.result.value); } // 第三种,键游标方法,该方法与第二种的差异为:普通游标带有value值表示获取的数据,而键游标没有 index.openKeyCursor().onsuccess = function (event) { console.log('openKeyCursor:', event.target.result); } }
当须要修改存储空间中的数据时,咱们可使用如下的API:
var objectStore = transaction.objectStore("customers"); var request = objectStore.put(data); request.onsuccess = function (event) { }
注:put
方法不只可以修改现有数据,也可以往存储空间中增长新的数据。
当咱们须要删除已经无用的数据时,咱们能够经过如下方法:
var objectStore = transaction.objectStore("customers"); var request = objectStore.delete(name); request.onsuccess = function (event) { }
在浏览器有以下操做的状况下,indexedDB可能会出现异常:
此时,须要对错误进行捕获,而且对用户进行提示。此章节不是本文重点,再此略过。
在IndexedDB
中,键值对中的key
值能够接受如下几种类型的值:
具体说明能够见文档此处。
当一个key
值变为主键,即keyPath
时,它的值就只能是如下几种:
注:空格不能出如今key path中。
具体说明能够见文档此处。
在IndexedDB
中,value可以接受ECMA-262中全部的类型的值,例如String,Date,ImageDate等。
IndexedDB
在没有指定key值的时候就会采用自增的key值。若是一个事务在中途中断,那么key值的自增将会从中断的事务开始前的key开始。
IndexedDB
也受到浏览器同源策略的限制。
用户在清除浏览器缓存时,可能会清除IndexedDB
中相关的数据。
部分浏览器如Safari手机版隐私模式在访问IndexedDB
时,可能会出现因为没有权限而致使的异常(LocalStorage也会),须要进行异常处理。
IndexedDB
在本地存储中有着无可替代的做用,是替代关系型数据库web sql
的产品,可以对大量数据进行存储。在许多须要运用离线存储的场景下,它可以给咱们提供有效的支撑。
可是,IndexedDB
在使用过程当中仍然须要避免可能会出现的一些问题,或者对可能致使的不利影响有必定的容错处理。这样才不会对应用产生重大影响。