You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
// 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;window.IDBKeyRange=window.IDBKeyRange||window.webkitIDBKeyRange||window.msIDBKeyRange// (Mozilla has never prefixed these objects, so we don't need window.mozIDB*)[/pre]
if(!window.indexedDB){window.alert("Your browser doesn't support a stable version of IndexedDB. Such and such feature will not be available.")}
该 open 方法接受第二个参数,就是数据库的版本号。这样我们就可以更新数据库的 schema ,也就是说如果我们打开的数据库不是我们期望的最新版本的话,我们可以对 object store 进行创建或是删除。
varrequest,database;request=indexedDB.open('admin');request.onerror=function(event){console.log('somthing bad happended while trying to open:'+event.target.errorCode);}request.onsuccess=function(event){database=event.target.result;console.log(database);}
vartransaction=db.transaction(["customers"]);varobjectStore=transaction.objectStore("customers");varrequest=objectStore.get("444-44-4444");request.onerror=function(event){// 错误处理!};request.onsuccess=function(event){// 对 request.result 做些操作!alert("Name for SSN 444-44-4444 is "+request.result.name);};
db.transaction("customers").objectStore("customers").get("444-44-4444").onsuccess=function(event){alert("Name for SSN 444-44-4444 is "+event.target.result.name);};
varobjectStore=db.transaction("customers").objectStore("customers");objectStore.openCursor().onsuccess=function(event){varcursor=event.target.result;if(cursor){alert("Name for SSN "+cursor.key+" is "+cursor.value.name);cursor.continue();}else{alert("No more entries!");}};
openCursor() 函数
openCursor() 函数需要几个参数如下:
你可以使用一个 key range 对象来限制被检索的项目的范围。
你可以指定你希望进行迭代的方向
在上面的示例中,我们在以升序迭代所有的对象。游标成功的回调有点特别。游标对象本身是请求的 result (上面我们使用的是简写形式,所以是 event.target.result)。
然后实际的 key 和 value 可以根据游标对象的 key 和 value 属性被找到。如果你想要保持继续前行,那么你必须调用游标上的 continue() 。
当你已经到达数据的末尾时(或者没有匹配 openCursor() 请求的条目)你仍然会得到一个成功回调,但是 result 属性是 undefined。
使用游标的一种常见模式是提取出在一个对象存储空间中的所有对象然后把它们添加到一个数组中,像这样:
varcustomers=[];objectStore.openCursor().onsuccess=function(event){varcursor=event.target.result;if(cursor){customers.push(cursor.value);cursor.continue();}else{alert("Got all customers: "+customers);}};
如果你想要限定你在游标中看到的值的范围,你可以使用一个 key range 对象然后把它作为第一个参数传给 openCursor() 或是 openKeyCursor()。
你可以构造一个只允许一个单一 key 的 key range,或者一个具有下限或上限,或者一个既有上限也有下限。边界可以是闭合的(也就是说 key range 包含给定的值)或者是“开放的”(也就是说 key range 不包括给定的值)。这里是它如何工作的:
// 只匹配 "Donna"varsingleKeyRange=IDBKeyRange.only("Donna");// 匹配所有在 "Bill" 前面的, 包括 "Bill"varlowerBoundKeyRange=IDBKeyRange.lowerBound("Bill");// 匹配所有在 “Bill” 前面的, 但是不需要包括 "Bill"varlowerBoundOpenKeyRange=IDBKeyRange.lowerBound("Bill",true);// Match anything up to, but not including, "Donna"varupperBoundOpenKeyRange=IDBKeyRange.upperBound("Donna",true);//Match anything between "Bill" and "Donna", but not including "Donna"varboundKeyRange=IDBKeyRange.bound("Bill","Donna",false,true);index.openCursor(boundKeyRange).onsuccess=function(event){varcursor=event.target.result;if(cursor){// Do something with the matches.cursor.continue();}};
objectStore.openCursor(null,IDBCursor.prev).onsuccess=function(event){varcursor=event.target.result;if(cursor){// Do something with the entries.cursor.continue();}};
index.openKeyCursor(null,IDBCursor.nextunique).onsuccess=function(event){varcursor=event.target.result;if(cursor){// Do something with the entries.cursor.continue();}};
当一个 web app 在另一个标签页中被打开时的版本变更
当你的 web app 在这样一种方式下改变你的数据库时碰到被要求进行版本变化,你需要考虑如果用户已经在一个标签页中打开了你的应用的旧版本的数据库,然后他又在另一个标签页中加载了你的应用的新版本,这种情况下会发生什么事情。当你带着比数据库实际版本更高的版本号调用 open() 时,所有其他打开的数据库必须在你开始实际对数据库进行修改之前显式通知这个请求。这里是它如何工作的:
varopenReq=mozIndexedDB.open("MyTestDatabase",2);openReq.onblocked=function(event){// 如果其他标签页已经加载了这个数据库,那么// 在我们可以继续处理之前它需要被关闭。alert("Please close all other tabs with this site open!");};openReq.onupgradeneeded=function(event){// 所有其它数据库都已经被关掉了。Set everything up.db.createObjectStore(/* ... */);useDatabase(db);}openReq.onsuccess=function(event){vardb=event.target.result;useDatabase(db);return;}functionuseDatabase(db){// 确保添加一个如果另一个页面请求一个版本变化时来被通知的处理程序。// 我们必须关闭这个数据库。这就允许其他页面对数据库进行升级。// 如果你不这么做的话,除非用户关闭标签页否则升级就不会发生。db.onversionchange=function(event){db.close();alert("A new version of this page is ready. Please reload!");};// 其他针对数据库的处理}
indexedDB
indexed Database API 或者简称为IndexedDB,是在浏览器中保存的结构化数据的一种数据库。IndexedDB是为了代替目前已废弃的Web SQL Database API而出现的。
IndexedDB设计的操作完全是异步进行的,因此,多数操作会以请求方式进行,但这些操作会在后期执行,然后如果成功则返回结果,如果失败则返回错误。差不多每一次IndexedDB操作,都需要注册onerror和onsuccess事件处理程序,以确保适当地处理结果。
由于 IndexedDB 本身的规范还在持续演进中,当前的 IndexedDB 的实现还是使用浏览器前缀。在规范更加稳定之前,浏览器厂商对于标准 IndexedDB API 可能都会有不同的实现。但是一旦大家对规范达成共识的话,厂商就会不带前缀标记地进行实现。实际上一些实现已经移除了浏览器前缀:IE 10,Firefox 16 和 Chrome 24。当使用前缀的时候,基于 Gecko 内核的浏览器使用 moz 前缀,基于 WebKit 内核的浏览器会使用 webkit 前缀等等
数据库
IndexedDB就是一个数据库,其最大的特色是使用对象保存数据,而不是使用表来保存数据。一个IndexedDB数据库,就是一组位于相同命名空间下的对象的集合。
使用IndexedDB的第一步就是打开它,即把要打开的数据库名传给
indexedDB.open()
。如果传入的数据库已经存在,就会发送一个打开它的请求;如果传入的数据库还不存在,就会发送一个创建并打开它的请求。indexDB.open()
会返回一个IDBRequest对象,在这个对象上可以添加onerror和onsuccess事件处理程序该 open 方法接受第二个参数,就是数据库的版本号。这样我们就可以更新数据库的 schema ,也就是说如果我们打开的数据库不是我们期望的最新版本的话,我们可以对 object store 进行创建或是删除。
在上面两个事件处理程序中,
event.target
都指向request对象,因此他们可以互相换用。如果响应onsuccess事件处理程序,那么event.target.result
中将有一个数据库实例对象(IDBData-base)。如果发生错误,那么event.target.errorCode
中将保存一个错误码,表示问题的性质。参见一下创建和更新数据库版本号
要更新数据库的 schema,也就是创建或者删除对象存储空间,需要实现 onupgradeneeded 处理程序,这个处理程序将会作为一个允许你处理对象存储空间的 versionchange 事务的一部分被调用。
在数据库第一次被打开时或者当指定的版本号高于当前被持久化的数据库的版本号时,这个 versionchange 事务将被创建
版本号是一个
unsigned long long
数字,这意味着它可以是一个非常大的整数。这也意味着你不能使用浮点数,否则它会被转换成最接近的较小的整数并且事务可能不会启动,同样 upgradeneeded 事件也不会作为一个结果返回。例如不要使用 2.4 作为版本:
构建数据库
现在来构建数据库。IndexedDB 使用对象存储空间而不是表,并且一个单独的数据库可以包含任意数量的对象存储空间。每当一个值被存储进一个对象存储空间时,它会被和一个键相关联。键的提供可以有几种不同的方法,这取决于对象存储空间是使用 key path 还是 key generator。
下面的表格显示了几种不同的提供键的方法。
你也可以使用对象存储空间持有的对象,不是基本数据类型,在任何对象存储空间上创建索引。索引可以让你使用被存储的对象的属性的值来查找存储在对象存储空间的值,而不是用对象的键来查找。
此外,索引具有对存储的数据执行简单限制的能力。通过在创建索引时设置 unique 标记,索引可以确保不会有两个具有同样索引 key path 值的对象被储存。因此,举例来说,如果你有一个用于持有一组 people 的对象存储空间,并且你想要确保不会有两个拥有同样 email 地址的 people,你可以使用一个带有 unique 标识的索引来确保这些。
正如前面提到的,onupgradeneeded 是我们唯一可以修改数据库结构的地方。在这里面,我们可以创建和删除对象存储空间以及构建和删除索引。
对象存储空间仅调用 createObjectStore() 就可以创建。这个方法使用存储空间的名称,和一个对象参数。
即便这个参数对象是可选的,它还是非常重要的,因为它可以让你定义重要的可选属性和完善你希望创建的对象存储空间的类型。
在我们的示例中,我们请求了一个名为“customers” 的对象存储空间并且定义了一个 使得存储空间中每个单独的对象都是唯一的属性作为 key path。
在这个示例中的属性是 “ssn”,被确保是唯一的。被存储在对象存储空间中的所有对象都必须存在“ssn”。
我们也请求了一个名为 “name” 的着眼于存储的对象的 name 属性的索引。如同 createObjectStore(),createIndex() 使用了一个完善了我们希望创建的索引类型的可选的 options 对象。添加一个不带 name 属性的对象也会成功,但是这个对象不会出现在 “name” 索引中。
增加和删除数据
在你可以对新数据库做任何事情之前,你需要开始一个事务。事务来自于数据库对象,而且你必须指定你想让这个事务跨越哪些对象存储空间。另外,你需要决定你是否将要对数据库进行更改或者你只是需要从它里面进行读取。虽然事务具有三种模式(只读,读写,和版本变更),在可以的情况下你最好还是使用只读事务,因为它们可以并发运行。
transaction()
transaction()
接受三个参数,(其中两个是可选的)并且返回一个事务对象。现在有了一个事务了,你将需要从它拿到一个对象存储空间。事务只能让你拿到一个你在创建事务时已经指定过的对象存储空间。然后你可以增加所有你需要的数据。
产生自 add() 调用的请求的 result 是被添加的值的键。因此在这种情况下,它应该等于被添加的对象的 ssn 属性, 因为对象存储空间使用 ssn 属性作为 key path。
注意 add() 函数要求数据库中不能已经有相同键的对象存在。如果你正在试图修改一个现有条目,或者你并不关心是否有一个同样的条目已经存在,使用 put()函数。
从数据库中删除数据
从数据库中获取数据
现在数据库里已经有了一些信息,你可以通过几种方法对它进行提取。首先是简单的 get()。你需要提供键来提取值,像这样:
对于一个“简单”的提取这里的代码有点多了。下面看我们怎么把它再缩短一点,假设你在数据库的级别上来进行的错误处理:
这是如何工作的呢?由于只有一个对象存储空间,你可以避免传入一个在你的事务中需要的对象存储空间的列表,而只是作为一个字符串把名字传入即可。
同样,你只是在从数据库读取数据,所以你不需要一个 "readwrite"事务。调用一个没有指定模式的 transaction()将给你一个 "readonly"事务。
这里的另外一个微妙之处在于你实际上不需要保存请求对象到一个变量。因为 DOM 事件把这个请求作为它的 target,你可以使用 event 来得到 result 属性。
使用游标
使用 get() 要求你知道你想要检索哪一个键。如果你想要遍历对象存储空间中的所有值,那么你可以使用游标。看起来会像下面这样:
openCursor() 函数
openCursor() 函数需要几个参数如下:
在上面的示例中,我们在以升序迭代所有的对象。游标成功的回调有点特别。游标对象本身是请求的 result (上面我们使用的是简写形式,所以是 event.target.result)。
然后实际的 key 和 value 可以根据游标对象的 key 和 value 属性被找到。如果你想要保持继续前行,那么你必须调用游标上的 continue() 。
当你已经到达数据的末尾时(或者没有匹配 openCursor() 请求的条目)你仍然会得到一个成功回调,但是 result 属性是 undefined。
使用游标的一种常见模式是提取出在一个对象存储空间中的所有对象然后把它们添加到一个数组中,像这样:
使用索引
使用 SSN 作为键来存储客户数据是合理的,因为 SSN 唯一地标识了一个个体(对隐私来说这是否是一个好的想法是另外一个话题,不在本文的讨论范围内)。如果你想要通过姓名来查找一个客户,那么,你将需要在数据库中迭代所有的 SSN 直到你找到正确的那个。以这种方式来查找将会非常的慢,相反你可以使用索引。
“name” 游标不是唯一的,因此 name 被设成 "Donna" 的记录可能不止一条。在这种情况下,你总是得到键值最小的那个。
如果你需要访问带有给定 name 的所有的记录你可以使用一个游标。你可以在索引上打开两个不同类型的游标。
指定游标的范围和方向
如果你想要限定你在游标中看到的值的范围,你可以使用一个 key range 对象然后把它作为第一个参数传给 openCursor() 或是 openKeyCursor()。
你可以构造一个只允许一个单一 key 的 key range,或者一个具有下限或上限,或者一个既有上限也有下限。边界可以是闭合的(也就是说 key range 包含给定的值)或者是“开放的”(也就是说 key range 不包括给定的值)。这里是它如何工作的:
有时候你可能想要以倒序而不是正序(所有游标的默认顺序)来遍历。切换方向是通过传递 prev 到 openCursor() 方法来实现的
因为 “name” 索引不是唯一的,那就有可能存在具有相同 name 的多条记录。要注意的是这种情况不可能发生在对象存储空间上,因为键必须永远是唯一的。如果你想要在游标在索引迭代过程中过滤出重复的,你可以传递 nextunique (或 prevunique 如果你正在向后寻找)作为方向参数。 当 nextunique 或是 prevunique 被使用时,被返回的那个总是键最小的记录。
当一个 web app 在另一个标签页中被打开时的版本变更
当你的 web app 在这样一种方式下改变你的数据库时碰到被要求进行版本变化,你需要考虑如果用户已经在一个标签页中打开了你的应用的旧版本的数据库,然后他又在另一个标签页中加载了你的应用的新版本,这种情况下会发生什么事情。当你带着比数据库实际版本更高的版本号调用 open() 时,所有其他打开的数据库必须在你开始实际对数据库进行修改之前显式通知这个请求。这里是它如何工作的:
数据库的删除
补充知识
indexedDB的API
IndexedDB
参考文章
《javascript高级程序设计》
使用 IndexedDB
HTML5的存储
The text was updated successfully, but these errors were encountered: