IndexedDB是一种可以让您在用户浏览器中持久保存数据的方法。因为它可以让您创建带有强大的数据检索能力的web应用,即使在离线情况下依然可用。
关于本文档
本教程让您轻松掌握IndexedDB异步API。如果您还不太熟悉IndexedDB的基础知识,请参考IndexedDB的基本概念。
关于IndexedDB API的参考文档,可以参看IndexedDBarticle and its subpages,列出了IndexedDB中使用的对象类型,同步API和异步API的方法(method)
使用IndexedDB的通常方法步骤:
1打开一个数据库
2在数据库中创建一个对象object store
3开启一个事务transaction并创建一个数据库操作请求request,比如插入数据、或者查询数据
4监听DOM事件,等待操作完成。
5对结果result执行一些处理(结果result在返回的request中)。
遵循这些重点步骤,我们可以得到更多的信息。
创建和结构化对象
因为(indexedDB的)规范还在不断完善中,当前版本的IndexedDB掩盖了在浏览器中被支持的前缀。在(indexedDB的)规范形成最后版本之前,浏览器生产商可能支持不
同版本的标准IndexedDB API。但是,一旦(indexedDB官方与浏览器生产商)达成了一致,生产商将实现无前缀的统一IndexedDB版本。实际上,一些版本已经实现了无前
缀(统一):Internet Explorer10,Firefox 16,Chrome 24。当使用前缀时,基于Gecko的浏览器使用moz前缀,基于WebKit的浏览器使用webkit前缀。
使用实验性(Meta)版本的IndexedDB
如果您想在依然需要前缀才能支持IndexedDB的浏览器中测试您的程序,您可以使用如下代码:
// In the following line,you should include the prefixes of implementations you want to test. window.indexedDB = window|| window.mozIndexedDB .webkitIndexedDB .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 .webkitIDBTransaction .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 .webkitIDBKeyRange .msIDBKeyRange// (Mozilla has never prefixed these objects,so we don't need window.mozIDB*)
请注意,带有前缀的版本可能是不正常、不完整的,或者参照了旧版本的规范。因此,不建议在正式产品中使用。
也许【明确表明不支持】要比【承诺了支持,但是却失败了】要更好一些。
if (!window.indexedDB) { window.alert("Your browser doesn't support a stable version of IndexedDB. Such and such feature will not be available."); }
打开数据库
我们这样开始:
// Let us open our database var request open"MyTestDatabase", 3;
注意,打开数据库正如其它任何的操作——您必须申请它。
【打开】申请不会打开马上数据库或者开启事务。呼叫Open()函数将返回一个IDBOpenDBRequest对象附带一个成功结果result,或者错误消息error,您能够通过事件event来操纵它。IndexedDB中多数其它的异步函数都在做同样的事情——返回一个IDBRequest对象附带一个成功结果result,或者错误消息error。Result是一个IDBDatabase实例。
Open函数的第二个参数是数据库的版本version。数据库的版本决定了数据库的模式——数据库中的对象store和他们的结构。如果数据库不存在,它会被open操作创建,然后触发onupgradeneeded事件,而且您将在该事件句柄中创建数据库模式。如果数据库存在,但是您指定了一个更高的版本,将触发onupgradeneeded事件,然后在该事件句柄中创建数据库模式。更多内容稍后在下面的Updating the version of the database和IDBFactory.open
中介绍。
重要:版本号是一个无符号长整型数字,表示它可以是一个很大的数字。不能用浮点型,否则将会被强制转换为最近似
的整型数,而且事务不会开启,upgradeneeded事件不会触发。例如,不要使用2.4作为一个版本号。
var request = indexedDB.open("MyTestDatabase",2.4);
创建句柄
对于您创建的几乎所有的请求,首先您需要做的是添加success和error句柄:
request.onerror = function(event{ // Do something with request.errorCode! ; request.onsuccess // Do something with request.result! ;
onsuccess()和onerror()哪个函数会被调用呢?如果操作成功了,将触发success事件(DOM 事件的属性值为‘success’)
,一旦触发了该事件,request 绑定的onsuccess()函数将被调用,并且把success事件作为参数。反之,如果发生了错误,则按照类型方式,触发error事件,以error事件作为参数调用onerror()函数。
IndexedDB API的设计努力让错误发生降至最少,因此您并不会看到很多的error事件。然而,打开数据库时,通常有一
些情况可能会导致错误发生。最有典型的错误情况是,用户不提供给您web应用许可权来创建数据库。IndexedDB的一个主要设计目标就是为离线应用而存储大量数据。(如需了解每个浏览器的的存储数据容量限制,请参考Storage limits)
很明显,浏览器不希望广告网站或者恶意网站充斥于您的电脑,因此,当有web应用为存储数据尝试打开IndexedDB数据库时,第一次浏览器将提示用户。用户可以选择允许或者禁止该行为。而且,浏览器隐私模式下,IndexedDB数据仅仅驻留在内存中,直到匿名模式被关闭(IndexedDB数据才会被存入磁盘)。(Firefox的隐私模式,chrome的匿名模式,但是对于Firefox,截止Nov 2015的版本,隐私模式都是不完善的,因此您无法在Firefox隐私模式下使用IndexedDB)
现在,假设用户允许您申请创建数据库,而且已经收到了成功事件success,并触发了对应函数。接下来要干什么呢,
indexedDB.open()返回了请求request,而request. Result是一个IDBDatabase的实例,您肯定想要保存它。您的代码可能看起来如下:
var db; = indexedDB{ "Why didn't you allow my web app to use IndexedDB?!"{ db = event.target.result;
错误处理
如上所述,错误事件冒泡。错误事件绑定在产生错误的request对象上,然后事件冒泡到事务,最终到了数据库对象database。如果您不想为每一个request都添加一个错误事件处理,您可以在数据库对象上添加一个的错误处理,如下
db// Generic error handler for all errors targeted at this database's // requests! "Database error: " + event.errorCode;
打开数据库时,最常见的错误是VER_ERR,表示已存在数据库的版本号大于指定的数据库版本号,该错误必须被处理
当您创建新的数据库或者升级已存在数据库的版本时(在打开数据库时,指定一个更高的版本号数字),onupgradeneeded事件将触发,IDBVersionChangeEvent对象将被传递给任何在request.result上设置的onversionchange事件处理(比如例子中的db)。upgradeneeded 事件处理中,您应该创建对象为该版本的数据库创建对象store
此时,数据库中已经拥有了以前版本的数据库的对象store,因此,您无需再创建这些对象store。您唯一需要做的就是
创建新增的对象store,或者删除以前版本中不需要的对象store。如果您需要改变已存在的对象store(比如,keypath)
您必须删除旧的对象store,然后创建新对象store(附带可选项)(注意,此操作将删除原有对象store中的数据,如果你需要这些信息,可以在升级数据库之前,将之读出数据库,保存到合适的位置)。
试图创建已存在的对象store(或者删除不存在的对象store)将引发错误
如果onupgradeneeded事件成功完成,当前的request对应的onsuccess处理程序将被触发
Blink/Webkit 支持当前版本的规范,包括Chrome 23+ 和 Opera17+; IE10+ 也支持。其它的和旧版本不支持当前版本的规范,因此也不支持indexedDB.open(name,version).onupgradeneeded。更多信息关于如何在旧版本Webkit/Blink浏览器升级数据库,请参考IDBDatabasereference article.
数据库结构化
现在介绍数据库结构化。IndexedDB使用对象store而不是表table,一个数据库可以包含任意多个对象store。当一个【值】value被存入对象store时,它总是与一个【键】key关联。根据对象store是使用key path还是keygenerator的情况,
Key Path (keyPath) |
Key Generator (升序描述 |
No |
对象store可以操纵任何类型的值,包括原生的值比如整数和字符串.一旦您插入新值,您必须提供独立的【键】声明. |
Yes |
只能操纵JavaScript. JavaScript对象必须有一个与key path同名的属性【键】是自动生成的,或者如果您想自己指定【键】,您可以提供一个独立的【键】声明。 |
您也可以为任何的对象store创建索引index,提供index对象来操纵对象store,而不是原生的方式。Index让您使用
被存储对象的属性值——而不是对象的【键】——来查询对象store中的值。还有,index可以在存储的数据上实施简
单的约束(constraints)。在创建index时,通过设置唯一标示符unique flag,index可以确保没有任何两个对象在index的key path上的值相同。例如,如果您有一个对象store操纵人口集合,而且您想确保没有任何两个人有相同的email地址,您可以在index设置唯一标示符unique flag来强制这一点。
这听起来有点复杂,但是这个简单的例子应该能解释这个概念。首先,我们定义一些用户数据以备使用:
// This is what our customer data looks like. const customerData = [ { ssn"444-44-4444""Bill": 35"bill@company.com" "555-55-5555""Donna"32"donna@home.org" } ];
当然, 您将不会使用某人的社会保障号作为客户表的主键,因为并不是每个人都有社会保障号,而且您将存储他们的
出生日期而不是他们的年龄,这里是为方便起见,请忽略繁琐的细节,把眼光放开。
现在,我们创建数据库。
const dbName = "the_name"; (dbName2; request// Handle errors. .onupgradeneeded { var db ; // 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 = dbcreateObjectStore"customers"{ keyPath"ssn" // Create an index to search customers by name. We may have duplicates // so we can't use a unique index. objectStorecreateIndex"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"email"true // Use transaction oncomplete to make sure the objectStore creation is // finished before adding data into it. objectStore.transaction.oncomplete { // Store values in the newly created objectStore. var customerObjectStore transactionobjectStore; for (var i in customerData{ customerObjectStoreadd(customerData[i; } ;
对象store 通过调用createObjectStore()创建。该方法需要对象store的名字,和一个参数对象。虽然参数对象是可选的,
但是它非常重要,因为可以让您定义重要的可选属性和完善新对象store的类型描述。在我们的例子中,创建了一个对
象store,名字为“customers”,定义了keypath属性,该属性可以让对象store中的对象能够唯一识别,在本例中,该
属性的值是ssn,因为ssn能够保证唯一性,每一个存入对象store的对象都必须提供ssn。
使用key generator
创建对象store时,设置升序标志autoIncrementflag,能够让激活key generator。默认不设置。
使用key generator,当您向对象store新增数据时,【键】将自动生成。对象store初建时,keygenerator的当前数字是1。
基本上,自动生成的新【键】比前一个【键】多1。keygenerator的当前数字编号从来不会减小,不会因为数据库操作
而恢复。比如,当事务中止。因此,当删除一条记录甚至清空整个对象store,对keygenerator也没有影响。
我们可以用key generator创建另一个对象store:
// Open the indexedDB. function { ; // Create another object store called "names" with the autoIncrement flag set as true. var objStore "names"{ autoIncrement // Because the "names" object store has the key generator,the key for the name value is generated automatically. // The added records would be like: // key : 1 => value : "Bill" // key : 2 => value : "Donna" { objStore.name;更多key generator的内容,请参考W3C Key Generators