aboutsummaryrefslogtreecommitdiff
path: root/files/zh-cn/web/api/indexeddb_api
diff options
context:
space:
mode:
authorPeter Bengtsson <mail@peterbe.com>2020-12-08 14:40:17 -0500
committerPeter Bengtsson <mail@peterbe.com>2020-12-08 14:40:17 -0500
commit33058f2b292b3a581333bdfb21b8f671898c5060 (patch)
tree51c3e392513ec574331b2d3f85c394445ea803c6 /files/zh-cn/web/api/indexeddb_api
parent8b66d724f7caf0157093fb09cfec8fbd0c6ad50a (diff)
downloadtranslated-content-33058f2b292b3a581333bdfb21b8f671898c5060.tar.gz
translated-content-33058f2b292b3a581333bdfb21b8f671898c5060.tar.bz2
translated-content-33058f2b292b3a581333bdfb21b8f671898c5060.zip
initial commit
Diffstat (limited to 'files/zh-cn/web/api/indexeddb_api')
-rw-r--r--files/zh-cn/web/api/indexeddb_api/basic_concepts_behind_indexeddb/index.html204
-rw-r--r--files/zh-cn/web/api/indexeddb_api/browser_storage_limits_and_eviction_criteria/index.html132
-rw-r--r--files/zh-cn/web/api/indexeddb_api/checking_when_a_deadline_is_due/index.html208
-rw-r--r--files/zh-cn/web/api/indexeddb_api/index.html155
-rw-r--r--files/zh-cn/web/api/indexeddb_api/using_indexeddb/index.html1340
-rw-r--r--files/zh-cn/web/api/indexeddb_api/using_indexeddb_in_chrome/index.html27
6 files changed, 2066 insertions, 0 deletions
diff --git a/files/zh-cn/web/api/indexeddb_api/basic_concepts_behind_indexeddb/index.html b/files/zh-cn/web/api/indexeddb_api/basic_concepts_behind_indexeddb/index.html
new file mode 100644
index 0000000000..dfbfa97dd6
--- /dev/null
+++ b/files/zh-cn/web/api/indexeddb_api/basic_concepts_behind_indexeddb/index.html
@@ -0,0 +1,204 @@
+---
+title: 基本概念
+slug: Web/API/IndexedDB_API/Basic_Concepts_Behind_IndexedDB
+tags:
+ - IndexedDB 总述
+translation_of: Web/API/IndexedDB_API/Basic_Concepts_Behind_IndexedDB
+---
+<p>{{DefaultAPISidebar("IndexedDB")}}</p>
+
+<div class="summary">
+<p><strong>IndexedDB </strong>是一种在用户浏览器中持久存储数据的方法。它允许您不考虑网络可用性,创建具有丰富查询能力的可离线 Web 应用程序。IndexedDB 对于存储大量数据的应用程序(例如借阅库中的 DVD 目录)和不需要持久 Internet 连接的应用程序(例如邮件客户端、待办事项列表或记事本)很有用。</p>
+</div>
+
+<h2 id="关于本文档">关于本文档</h2>
+
+<p>本简介讨论了 IndexedDB 中的基本概念和术语。为您提供了概览并解释了关键概念。</p>
+
+<p>您会发现以下非常有用的内容:</p>
+
+<ul>
+ <li>有关 IndexedDB 的设计和结构的概述,请参阅<a href="#concepts">概览</a>。</li>
+ <li>要了解有关 IndexedDB 术语的更多信息,请参阅<a href="#definitions">定义</a>部分。</li>
+ <li>有关如何使用 API​​ 的详细教程,请参阅<a href="/zh-CN/docs/Web/API/IndexedDB_API/Using_IndexedDB">使用 IndexedDB</a>。</li>
+ <li>有关 IndexedDB API 的参考文档,请参阅主要的 <a href="/zh-CN/docs/Web/API/IndexedDB_API">IndexedDB API</a> 文章及其子页面,这些文章记录了 IndexedDB 使用的对象类型。</li>
+ <li>有关浏览器如何处理在后台存储数据的更多信息,请阅读<a href="/en-US/docs/Web/API/IndexedDB_API/Browser_storage_limits_and_eviction_criteria">浏览器存储限制和清理标准</a>。</li>
+</ul>
+
+<h2 id="IndexedDB概况"> IndexedDB概况</h2>
+
+<p>使用IndexedDB,你可以使用一个key作为索引进行存储或者获取数据。 你可以在事务(transaction)中完成对数据的修改。和大多数web存储解决方案相同,indexedDB也遵从同源协议(<a class="external" href="http://www.w3.org/Security/wiki/Same_Origin_Policy">same-origin policy</a>). 所以你只能访问同域中存储的数据,而不能访问其他域的。</p>
+
+<p>IndexedDB 是一种异步(<a href="/zh-cn/IndexedDB#Asynchronous_API">asynchronous</a>) API,异步API适用于大多数情况,包括<a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers" title="https://developer.mozilla.org/En/Using_web_workers">Web Workers</a>。因为在<a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers" title="https://developer.mozilla.org/En/Using_web_workers">Web Workers</a>上的使用,它过去也有一个同步(<a href="https://developer.mozilla.org/en/IndexedDB#Synchronous_API" title="https://developer.mozilla.org/en/IndexedDB#Synchronous_API">synchronous</a>)的版本,但是因为缺少web社区的支持,它已经被从规范中移除了。</p>
+
+<p>IndexedDB 过去有一个竞争规范—— WebSQL 数据库,但是W3C组织在2010年11月18日废弃了webSql。尽管两者都是存储的解决方案,但是他们提供的不是同样的功能。IndexedDB 和WebSQL的不同点在于WebSQL 是关系型数据库访问系统,IndexedDB 是索引表系统(key-value型)。</p>
+
+<h2 id="基本概念">基本概念</h2>
+
+<p>如果你因为使用其他类型数据库有某些固定思维,那么你在使用IndexedDB的时候可能会感到困惑,所以请牢记以下重要的概念:</p>
+
+<ul>
+ <li>
+ <p><strong>IndexedDB 数据库使用 key-value 键值对储存数据.</strong> values 数据可以是结构非常复杂的对象,key可以是对象自身的属性。你可以对对象的任何属性创建索引(index)以实现快速查询和列举排序。key可以是二进制对象。</p>
+ </li>
+ <li>
+ <p><strong>IndexedDB 是事务模式的数据库</strong>. 任何操作都发生在事务(<a href="#gloss_transaction">transaction)</a>中。  IndexedDB API提供了索引(indexes)、表(tables)、指针(cursors)等等,但是所有这些必须是依赖于某种事务的。因此,你不能在事务外执行命令或者打开指针。事务(transaction)有生存周期,在生存周期以后使用它会报错。并且,事务(transaction)是自动提交的,不可以手动提交。</p>
+
+ <p>当用户在不同的标签页同时打开Web应用的两个实例时,这个事务模型就会非常有用。如果没有事务操作的支持,这两个实例就会互相影响对方的修改。如果你不熟悉数据库的事务模型,请参考<a class="link-https" href="https://zh.wikipedia.org/wiki/%E6%95%B0%E6%8D%AE%E5%BA%93%E4%BA%8B%E5%8A%A1">数据库事务(维基百科)</a>。 同时参考<a href="https://en.wikipedia.org/wiki/Database_transaction">transaction</a>文章中关于事务的定义部分。</p>
+ </li>
+ <li>
+ <p><strong>The IndexedDB API 基本上是异步的。</strong> IndexedDB的API不通过return语句返回数据,而是需要你提供一个回调函数来接受数据。执行API时,你不以同步(synchronous)方式对数据库进行“存储”和“读取”操作,而是向数据库发送一个操作“请求”。当操作完成时,数据库会以DOM事件的方式通知你,同时事件的类型会告诉你这个操作是否成功完成。这个过程听起来会有些复杂,但是里面是有明智的原因的。这个和<a href="/zh-cn/DOM/XMLHttpRequest">XMLHttpRequest</a>请求是类似的</p>
+ </li>
+ <li>
+ <p><strong>IndexedDB数据库“请求”无处不在。 </strong>我们上边提到,数据库“请求”负责接受成功或失败的DOM事件。每一个“请求”都包含<code>onsuccess</code>和<code>onerror事件</code>属性,同时你还对“事件”调用addEventListener()和removeEventListener()。“请求”还包括<code>readyState,</code><code>result</code>和<code>errorCode属性,用来表示“请求”的状态。result属性尤其神奇,他可以根据“请求”生成的方式变成不同的东西,例如:</code>IDBCursor实例、刚插入数据库的数值对应的键值(key)等<code>。</code></p>
+ </li>
+ <li>
+ <p><strong>IndexedDB在结果准备好之后通过DOM事件通知用户。</strong> DOM事件总是有一个类型(<code>type</code>)属性(在IndexedDB中,该属性通常设置为<code>success</code>或<code>error</code>)。DOM事件还有一个目标(<code>target</code>)属性,用来告诉事件是被谁触发的。通常情况下,目标(<code>target</code>)属性是数据库操作生成的IDBRequest。成功(success)事件不弹出提示并且不能撤销,错误(error)事件会弹出提示且可以撤销。这一点是非常重要的,因为除非错误事件被撤销,否则他们会终止所在的任何事务。</p>
+ </li>
+ <li>
+ <p><strong>IndexedDB是面向对象的。</strong>indexedDB不是用二维表来表示集合的关系型数据库。这一点非常重要,将影响你设计和建立你的应用程序。</p>
+
+ <p>传统的关系型数据库,你需要用到二维表来存储数据集合(每一行代表一个数据,每一列代表一个属性),indexedDB有所不同,它要求你为一种数据创建一个对象仓库(object Store),只要这种数据是一个JavaScript对象即可。每个对象仓库都有一个索引(index)集合以方便查询和迭代遍历。如果你不熟悉面向对象的数据库管理系统,可以参考<a href="https://en.wikipedia.org/wiki/Object_database">维基百科有关对象数据库的内容</a></p>
+ </li>
+ <li>
+ <p><strong>indexedDB不使用结构化查询语言(SQL)。</strong>它通过索引(index)所产生的指针(cursor)来完成查询操作,从而使你可以迭代遍历到结果集合。如果你不熟悉NoSQL系统,可以参考<a href="https://en.wikipedia.org/wiki/Object_database">维基百科相关文章</a>。</p>
+ </li>
+ <li>
+ <p><strong>IndexedDB遵循同源(same-origin)策略</strong> “源”指脚本所在文档URL的域名、应用层协议和端口。每一个“源”都有与其相关联的数据库。在同一个“源”内的所有数据库都有唯一、可区别的名称。</p>
+
+ <p>IndexedDB的安全机制避免应用访问非同“源”的数据。例如,<a class="external" href="http://www.example.com/app/" rel="freelink">http://www.example.com/app/</a>的应用或页面可以访问<a class="external" href="http://www.example.com/dir/" rel="freelink">http://www.example.com/dir/</a>的数据,因为他们同“源”。但是它们不能访问<a class="external" href="http://www.example.com:8080/dir/" rel="freelink">http://www.example.com:8080/dir/</a>(不同端口)或<a class="link-https" href="https://www.example.com/dir/" rel="freelink">https://www.example.com/dir/</a>(不同协议)的数据,因为他们不同“源”。</p>
+ </li>
+ <li>
+ <div class="blockIndicator note">
+ <p>注意:第三方窗口上下文(例如HTML元素<strong>iframe</strong>)可以访问它所嵌入的网页的IndexedDB,除非浏览器被设置为<a href="https://support.mozilla.org/en-US/kb/disable-third-party-cookies">never accept third party cookies</a> (see {{bug("1147821")}}.)</p>
+ </div>
+ </li>
+</ul>
+
+<h2 id="名词解释">名词解释</h2>
+
+<p>本节定义并解释了IndexedDB API中所使用的术语</p>
+
+<h3 id="数据库">数据库</h3>
+
+<dl>
+ <dt><strong>数据库(database)</strong></dt>
+ <dd>一个信息库,通常包含一个或多个 <a href="/zh-CN/docs/IndexedDB/Basic_Concepts_Behind_IndexedDB$edit#gloss_object_store">object stores</a>. 每个数据库必须包含以下内容:</dd>
+ <dd>
+ <ul>
+ <li>名字(Name)。它标识了一个特定源中的数据库,并且在数据库的整个生命周期内保持不变。  此名字可以为任意字符串值(包括空字符串)。</li>
+ <li>当前版本(<a href="/zh-CN/docs/IndexedDB/Basic_Concepts_Behind_IndexedDB$edit#gloss_version">version</a>)。当一个数据库首次创建时,它的 version 为1,除非另外指定. 每个数据库在任意时刻只能有一个 version。</li>
+ </ul>
+ </dd>
+ <dt>持久性(durable)</dt>
+ <dd>在 Firefox 中,IndexedDB 是持久的,也就是说在一个读写事务中,一旦 <code>IDBTransaction.oncomplete</code> 事件被触发,就代表着数据已经确保被写入磁盘中了。</dd>
+ <dd>从 Firfox 40 起,IndexedDB 事务放松了对持久性的保证以提高性能(参见 <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1112702">Bug1112702</a>),这与其他支持 IndexedDB 的浏览器的处理方式相同。在这种情况下,当操作系统被告知去写入数据后 <code>complete</code> 事件便被触发,但此时数据可能还没有真正的写入磁盘。事件触发因此变得更快,但这样会有极小的机会发生以下情况:如果操作系统崩溃或在数据被写入磁盘前断电,那么整个事务都将丢失。由于这种灾难事件是罕见的,大多数使用者并不需要过分担心。</dd>
+ <dt>对象仓库(object store)</dt>
+ <dd>
+ <p>数据在数据库中存储的方式, 数据以键值对形式被对象仓库永久持有。对象仓库中的的数据以 <em><a href="#gloss_key">keys</a></em> 升序排列。</p>
+
+ <p>每一个对象仓库在同一个数据库中必须有唯一的名字。对象存储可以有一个 <em><a href="#gloss_keygenerator">key generator</a></em> 和一个 <em><a href="#gloss_keypath">key path</a>。</em>如果对象仓库有 key path,则使用 <em><a href="#gloss_inline_key">in-line keys</a></em>; 否则使用 <em><a href="#gloss_outofline_key">out-of-line keys</a>。</em></p>
+
+ <p>关于对象仓库的详细文档,请参考 <a href="../../../../zh-cn/IndexedDB/IDBObjectStore" rel="internal">IDBObjectStore</a> 或者 <a href="../../../../zh-cn/IndexedDB/IDBObjectStoreSync" rel="internal">IDBObjectStoreSync</a>。</p>
+ </dd>
+ <dt>版本(version)</dt>
+ <dd>当第一次创建一个数据库,它的版本为整型1。每个数据库依次有一个版本号; 一个数据库不能同时存在多个版本号。改变版本的唯一方法是通过一个比当前版本号更高的值去打开数据库。这会开启一个 <code>VERSION_CHANGE</code> 事务并且触发 <code>upgradeneeded</code> 事件。只有在该事件的处理函数中才能更新数据库模式。</dd>
+ <dd>注意:这里的描述以<a href="http://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html">最新规范</a>为准,这些规范可能只在最新的浏览器中实现了。老旧的浏览器实现了现在已弃用和移除的 <code>IDBDataBase.setVersion()</code> 方法。</dd>
+ <dt>数据库连接(database connection)</dt>
+ <dd>通过打开数据库创建的操作。一个给定的数据库可以同时拥有多个连接。</dd>
+ <dt>事务(transaction)</dt>
+ <dd>在一个特定的数据库上,一组具备原子性和持久性的数据访问和数据修改的操作。它是你与数据库交互的方式。并且,任何对于数据库中的数据读和修改的操作只能在事务中进行。</dd>
+ <dd>
+ <p>一个数据库连接可以拥有多个与之关联的事务,只要进行写操作事务的<a href="#">作用域</a>不相互重合。事务的作用域在事务被创建时就被确定,指定事务能够进行交互的对象仓库(object store),作用域一旦被确定就会在整个生命周期中保持不变。举个例子,如果一个数据库连接已经有了一个进行写操作的事务,其作用域覆盖 <code>flyingMonkey</code> 对象仓库,你可以开启新的事务其作用于 <code>unicornCentaur</code> 和 <code>unicornPegasus</code> 对象仓库。对于读操作的事务,你可以同时拥有多个,即使他们有重叠的作用域。</p>
+
+ <p>事务被期望拥有较短的生命周期,所以浏览器会终止一个消耗时间过长的事务,为了释放存储资源,运行过久的事务会被锁定。你可以中断一个事务,来回滚事务中对数据库进行的操作。并且你甚至不需要等待事务开始或激活就可以中断它。</p>
+
+ <p>事务有三种模式:读写、只读和版本变更。创建和删除对象仓库(object store)的唯一方法就是通过调用版本变更事务。了解更多关于事务类型的内容,请参考 <a href="/zh-cn/IndexedDB">IndexedDB</a>。</p>
+
+ <p>因为所有的事情都在事务中发生,所以它是 <a href="../../../../zh-cn/IndexedDB/IDBTransaction">IndexedDB</a> 中非常重要的一个概念。了解更多关于事务,尤其是关于它和版本控制的关联,查看 <a href="../../../../zh-cn/IndexedDB/IDBTransaction">IDBTransaction</a> 中的参考文档。关于同步接口的文档,查看 <a href="../../../../zh-cn/IndexedDB/IDBTransactionSync">IDBTransactionSync</a>。</p>
+ </dd>
+ <dt>请求(request)</dt>
+ <dd>在数据库上进行读写操作完成后的操作。每一个请求代表一个读或写操作。</dd>
+ <dt><a id="索引(index)" name="索引(index)">索引(index)</a></dt>
+ <dd>
+ <p>一个对象仓库,专门用来查找另一个对象仓库(object store)中的记录,其中被查找的对象仓库被称为引用对象仓库。索引(index)是一个稳定的键值对(key-value)存储,其记录中的值(value)是引用对象仓库记录中的键(key)。当引用对象仓库中的记录新增、更新或删除时,索引中的记录会自动的进行粒子性增加。索引中的每一条记录只能指向引用对象仓库中的一条记录,但多个索引可以引用同一个对象仓库。当对象仓库发生改变时,所有引用该对象仓库的引用均会自动更新。</p>
+
+ <p>可选地,你也可以适用<a href="#键(key)">键(key)</a>再对象仓库中查找记录。</p>
+
+ <p>了解更多关于如何适用索引,查看<a href="/zh-cn/IndexedDB/Using_IndexedDB#Using_an_index">使用 IndexedDB</a>。index 的参考文档查看<a href="../../../../zh-cn/IndexedDB/IDBKeyRange"> IDBKeyRange</a>。</p>
+ </dd>
+</dl>
+
+<h3 id="键和值">键和值</h3>
+
+<dl>
+ <dt><a id="键(key)" name="键(key)">键(key)</a></dt>
+ <dd>在对象仓库中阻止和检索被存储起来的值的数据值。数据仓库的键来源于以下三个方式之一:键生成器、键路径和显式指定的值。键必须是一种能够比较大小的数据类型。在同一个对象仓库中每条记录必须有一个独一无二的键,所以你不能在同一个对象仓库中为多个记录设置同样的键。</dd>
+ <dd>
+ <p>键可以是以下数据类型:字符串、日期、浮点和数组。对于数组,键的取值可以从空数组到无穷。并且你可以使用嵌套数组。注意,在 Firefox 11 之前的版本键只接受字符串和整形。</p>
+
+ <p>可选地,你也可以通过<a href="#索引(index)">索引(index)</a>来查找记录。</p>
+ </dd>
+ <dt>键生成器(key generator)</dt>
+ <dd>一种生成有序键的机制。如果一个对象仓库并不具备一个键生成器,那么应用程序必须为被存储的记录提供键。生成器在仓库之间并不共享。它更多的是浏览器的实现细节,因为在 Web 开发中你并不会真正的去创建或访问键生成器。</dd>
+ <dt>内键(in-line key)</dt>
+ <dd>作为存储值一部分的键。内键由键路径(key path)查找。内键由生成器生成。当内键生成后,它会被键路径存储在值中,它也可以被当作键使用。</dd>
+ <dt>外键(out-of-line key)</dt>
+ <dd>与值分开存储的键。</dd>
+ <dt>键路径(key path)</dt>
+ <dd>指定浏览器如何从对象仓库或索引存储的值中提取键。一个合法的键路径可以是以下形式:一个空字符串,一个 JavasScript 标识符,或由句点分割的多个 JavaScript 标识符。但不能包括空格。</dd>
+ <dt>值(value)</dt>
+ <dd>每条记录包含一个值,该值可以包含任何 JavaScript 表达式,包括:<a href="/zh-cn/JavaScript/Reference/Global_Objects/Boolean">布尔</a>、<a href="/zh-cn/JavaScript/Reference/Global_Objects/Number">数字</a>、<a href="/zh-cn/JavaScript/Reference/Global_Objects/String">字符串</a>、<a href="/zh-cn/JavaScript/Reference/Global_Objects/Date">日期</a>、<a href="/zh-cn/JavaScript/Reference/Global_Objects/Object">对象</a>、<a href="/zh-cn/JavaScript/Reference/Global_Objects/Array">数组</a>、<a href="/zh-cn/JavaScript/Reference/Global_Objects/RegExp">正则</a>、<a href="/zh-CN/docs/">未定义</a>和 null。</dd>
+ <dd>
+ <p>对于对象和数组,它们的属性和值也可以是任意合法的值。</p>
+
+ <p><a href="http://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html">规范</a>允许你存储文件和<a href="/zh-cn/DOM/Blob">二进制对象</a>,但该标准只被 Firefox 11+ 实现。</p>
+ </dd>
+</dl>
+
+<h3 id="范围和作用域">范围和作用域</h3>
+
+<dl>
+ <dt>作用域(scope)</dt>
+ <dd>事务所作用的一组对象仓库或索引。只读事务的作用域可以相互重叠并同时执行操作。但写操作事务的作用域不可以相互重叠。但你仍然可以同时开启多个拥有相同作用域的事务,只要保证他们的操作不会同时执行。</dd>
+ <dt>游标(cursor)</dt>
+ <dd>在键的某个范围内迭代查询多条记录的机制。游标有一个指向正在被迭代的对象仓库或索引的源。它处于该范围内的一个位置,并按照键的顺序正向或逆向的移动。有关游标的参考文档,查看 <a href="../../../../zh-cn/IndexedDB/IDBCursor">IDBCursor</a> 或 <a href="../../../../zh-cn/IndexedDB/IDBCursorSync">IDBCursorSync</a>。</dd>
+ <dt>键范围(key range)</dt>
+ <dd>用做键的数据类型上的连续的间隔。使用键或键的某个范围可以从对象仓库和索引中读取记录。你可以通过上限和下限设置和筛选范围。比如,你可以遍历 x 和 y 之间所有的键值。</dd>
+ <dd>有关键范围的参考文档,查看 <a href="../../../../zh-cn/IndexedDB/IDBKeyRange">IDBKeyRange</a>.</dd>
+</dl>
+
+<h2 id="局限性">局限性</h2>
+
+<p>以下情况不适合使用IndexedDB</p>
+
+<ul>
+ <li>全球多种语言混合存储。国际化支持不好。需要自己处理。</li>
+ <li>和服务器端数据库同步。你得自己写同步代码。</li>
+ <li>全文搜索。IndexedDB 接口没有类似 SQL 语句中 <code>LIKE</code> 的功能。</li>
+</ul>
+
+<p>注意,在以下情况下,数据库可能被清除:</p>
+
+<ul>
+ <li>用户请求清除数据。</li>
+ <li>浏览器处于隐私模式。最后退出浏览器的时候,数据会被清除。</li>
+ <li>硬盘等存储设备的容量到限。</li>
+ <li>数据损坏。</li>
+ <li>进行与特性不兼容的操作。</li>
+</ul>
+
+<p>确切的环境和浏览器特性会随着时间改变,但浏览器厂商通常会遵循尽最大努力保留数据的理念。</p>
+
+<h2 id="下一步">下一步</h2>
+
+<p>查看如何使用的文档: <a href="/zh-cn/IndexedDB/Using_IndexedDB">Using IndexedDB</a>.</p>
+
+<h2 id="相关文章">相关文章</h2>
+
+<ul>
+ <li>{{ spec("http://www.w3.org/TR/IndexedDB/", "Indexed Database API Specification", "WD") }}</li>
+ <li><a href="/zh-cn/IndexedDB">IndexedDB API Reference</a></li>
+ <li><a href="/zh-cn/IndexedDB/Using_IndexedDB">Using IndexedDB</a></li>
+ <li><a class="external" href="http://msdn.microsoft.com/en-us/scriptjunkie/gg679063.aspx">IndexedDB — The Store in Your Browser</a></li>
+</ul>
diff --git a/files/zh-cn/web/api/indexeddb_api/browser_storage_limits_and_eviction_criteria/index.html b/files/zh-cn/web/api/indexeddb_api/browser_storage_limits_and_eviction_criteria/index.html
new file mode 100644
index 0000000000..7934d15e70
--- /dev/null
+++ b/files/zh-cn/web/api/indexeddb_api/browser_storage_limits_and_eviction_criteria/index.html
@@ -0,0 +1,132 @@
+---
+title: IndexedDB 浏览器存储限制和清理标准
+slug: Web/API/IndexedDB_API/Browser_storage_limits_and_eviction_criteria
+tags:
+ - Database
+ - IndexedDB
+ - LRU
+ - Storage
+ - client-side
+ - eviction
+ - limit
+translation_of: Web/API/IndexedDB_API/Browser_storage_limits_and_eviction_criteria
+---
+<div>{{DefaultAPISidebar("IndexedDB")}}</div>
+
+<p class="summary"><font>有许多Web技术可以在客户端(即本地磁盘上)存储这种或那种数据。</font><font>浏览器计算分配给Web数据存储的空间大小以及达到该限制时要删除的内容的过程并不简单,并且浏览器之间有所不同。</font><font>本文介绍了浏览器如何确定要清除的本地内容以及何时释放所需的本地存储空间。</font></p>
+
+<div class="note">
+<p><span style="font-size: 14px; line-height: 21px;"><strong>注意</strong></span>: <font><font>对于大多数现代浏览器,以下信息应该相当准确,但在已知的情况下会调出特定于浏览器的信息。</font><font>Opera和Chrome在所有情况下都应该表现相同。</font></font><a href="http://www.opera.com/mobile/mini" rel="noopener"><font><font>Opera Mini</font></font></a><font><font>(仍然是基于presto的,服务器端呈现)不会在客户端上存储任何数据。</font></font></p>
+</div>
+
+<h2 id="什么技术使用浏览器数据存储?"><font><font>什么技术使用浏览器数据存储?</font></font></h2>
+
+<p><font><font>在Firefox中,以下技术利用浏览器数据存储在需要时存储数据。</font><font>在这种情况下,我们将它们称为“配额客户”:</font></font></p>
+
+<ul>
+ <li><a href="/zh-CN/docs/Web/API/IndexedDB_API">IndexedDB</a></li>
+ <li><a href="http://asmjs.org/" rel="noopener"><font><font>asm.js</font></font></a><font><font>缓存</font></font></li>
+ <li><a href="/zh-CN/docs/Web/API/Cache">缓存API</a></li>
+ <li>Cookies</li>
+</ul>
+
+<div class="blockIndicator note">
+<p><strong><font><font>注意</font></font></strong><font><font>:在Firefox中,</font></font><a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API"><font><font>Web Storage</font></font></a><font><font>也将很快开始使用相同的存储管理工具,如本文档中所述。</font></font></p>
+</div>
+
+<div class="blockIndicator note">
+<p><strong><font><font>注意</font></font></strong><font><font>:在隐私浏览模式下,大多数数据存储不被支持。</font><font>本地存储数据和cookie仍然可用,但它们是短暂的 ——当关闭最后一个隐私浏览窗口时,数据将被删除。</font></font></p>
+</div>
+
+<p><font><font>源的“最后访问时间”会更新,当其中任何一个被激活/停用时——所有这些源下的配额客户端的数据会被回收。</font></font></p>
+
+<p><font><font>在Chrome/Opera中,Quota Management API处理<a href="/zh-CN/docs/Web/HTML/Using_the_application_cache">AppCache</a>,<a href="/zh-CN/docs/Web/API/IndexedDB_API">IndexedDB</a>,WebSQL和<a href="/zh-CN/docs/WebGuide/API/File_System">File System API</a>的配额管理。</font></font></p>
+
+<h2 id="数据存储的不同类型">数据存储的不同类型</h2>
+
+<p>即使在相同的浏览器、使用相同的存储方法,仍然存在不同的数据存储方法需要我们搞清楚。这部分我们讨论那些在不同浏览器之间的不同之处。</p>
+
+<p>一般来说,数据存储的的类型主要有以下两种:</p>
+
+<ul>
+ <li>持久化存储:这种数据是希望长久保留的,只有的当用户选择清除才会被删除掉(比如,<font><font>在Firefox中,您可以通过转到</font></font><em><font><font>“首选项”</font></font></em><font><font>并使用“ </font></font><em><font><font>隐私和安全”&gt;“Cookie和站点数据”</font></font></em><font><font>下的选项,</font><font>选择删除所有存储的数据或仅删除所选来源的存储数据</font></font>)。</li>
+ <li>临时存储:这种数据不用保存很久,当最近一次使用时{{anch("Storage limits")}}达到限制大小就会被自动清理掉({{anch("LRU policy")}})。</li>
+</ul>
+
+<p><font>在Firefox中,当使用持久存储时,会向用户提供一个UI弹出窗口,提醒他们这些数据将持续存在,并询问他们是否对此感到满意。</font><font>临时数据存储不会引发任何用户提示。</font></p>
+
+<p><font>默认的是临时存储;开发人员可以选择使用</font>{{domxref("StorageManager.persist()")}}方法使用持久储存。</p>
+
+<h2 id="数据存储在哪里?"><font><font>数据存储在哪里?</font></font></h2>
+
+<p><font>每种存储类型代表一个单独的存储库。</font><font>这是用户Firefox配置文件下目录的实际映射(其他浏览器可能略有不同):</font></p>
+
+<ul>
+ <li><code>&lt;profile&gt;/storage</code> — 配额管理器维护的主要顶级目录(见下文)</li>
+ <li><code>&lt;profile&gt;/storage/permanent</code> — 持久数据存储库</li>
+ <li><code>&lt;profile&gt;/storage/temporary</code> —临时数据存储库</li>
+ <li><code>&lt;profile&gt;/storage/default</code> — 默认数据存储库</li>
+</ul>
+
+<div class="blockIndicator note">
+<p><strong><font><font>注意</font></font></strong><font><font>:引入<a href="/zh-CN/docs/Web/API/Web_Storage_API">Storage API</a>后,“permanent”文件夹可以被认为是过时的;“permanent”文件夹仅存储IndexedDB持久性数据库。</font><font>模式是“</font></font>best-effort<font><font>”还是“</font></font>persistent<font><font>”并不重要——数据存储在&lt;profile&gt;/storage/default下。</font></font></p>
+</div>
+
+<div class="blockIndicator note">
+<p><strong><font><font>注意</font></font></strong><font><font>:在Firefox中,可以通过</font></font><code>about:support</code><font><font>在URL栏中</font><font>输入</font><font>,然后按</font><font>“ </font><em><font>配置文件夹”</font></em><font>标题</font><font>旁边</font><font>的</font></font><em><font><font>“在...中显示...”</font></font></em><font><font>按钮(例如,</font><font>在Mac OS X上的</font></font><em><font><font>“在Finder中显示”)来查找</font></font></em><font><font>您的</font></font><em><font><font>配置文件文件夹</font></font></em><font><font>。</font></font></p>
+</div>
+
+<div class="blockIndicator note">
+<p><strong><font><font>注意</font></font></strong><font><font>:如果您在存储的数据中查看配置文件,您可能会看到第四个文件夹:</font></font><code>persistent</code><font><font>。</font><font>基本上,该</font></font><code>persistent</code><font><font>文件夹刚刚重命名为</font></font><code>permanent</code><font><font>以保持升级/迁移更简单。</font></font></p>
+</div>
+
+<p><strong><font><font>注意</font></font></strong><font><font>:用户不应在其下添加自己的目录或文件到</font></font><code>&lt;profile&gt;/storage</code>下<font><font>。</font><font>这将导致存储初始化失败;</font><font>例如,</font></font>{{domxref("IDBFactory.open()","open()")}}会<font><font>触发错误事件。</font></font></p>
+
+<h3 id="储存限制">储存限制</h3>
+
+<p>浏览器的最大存储空间是动态的——它取决于您的硬盘大小。 <strong>全局限制</strong>为可用磁盘空间的50%。 在Firefox中,一个名为Quota Manager的内部浏览器工具会跟踪每个源用尽的磁盘空间,并在必要时删除数据。</p>
+
+<p>因此,如果您的硬盘驱动器是500GB,那么浏览器的总存储容量为250GB。如果超过此范围,则会发起称为<strong>源回收</strong>的过程,删除整个源的数据,直到存储量再次低于限制。删除源数据没有只删一部分的说法——因为这样可能会导致不一致的问题。</p>
+
+<p>还有另一个限制称为<strong>组限制</strong>——这被定义为全局限制的20%,但它至少有10 MB,最大为2GB。 每个源都是一组(源组)的一部分。 每个eTLD+1域都有一个组。 例如:</p>
+
+<ul>
+ <li><code>mozilla.org</code>——组1,源1</li>
+ <li><code>www.mozilla.org</code>——组1,源2</li>
+ <li><code>joe.blogs.mozilla.org</code>——组1,源3</li>
+ <li><code>firefox.com</code> ——组2,源4</li>
+</ul>
+
+<p>在这个组中,<code>mozilla.org</code>、<code>www.mozilla.org</code>和<code>joe.blogs.mozilla.org</code>可以聚合使用最多20%的全局限制。 firefox.com单独最多使用20%。</p>
+
+<p>达到限制后有两种不同的反应:</p>
+
+<ul>
+ <li>组限制也称为“硬限制”:它不会触发源回收。</li>
+ <li>全局限制是一个“软限制”,因为其有可能释放一些空间并且这个操作可能持续。</li>
+</ul>
+
+<div class="blockIndicator note">
+<p>注意:尽管上面提到了最小组限制,但组限制不能超过全局限制。如果您的内存非常低,全局限制为8 MB,则组限制也将为8 MB。</p>
+</div>
+
+<div class="blockIndicator note">
+<p>注意:如果超出组限制,或者如果原因驱逐无法释放足够的空间,浏览器将抛出<code>QuotaExceededError</code>错误。</p>
+</div>
+
+<div class="blockIndicator note">
+<p>注意:在Chrome中,自M66以来,软硬存储配额限制已发生变化。 更多信息可以在<a href="https://chromium.googlesource.com/chromium/src/+/refs/heads/master/storage/browser/quota/quota_settings.cc#68">这里</a>找到。</p>
+</div>
+
+<h2 id="LRU策略">LRU策略</h2>
+
+<p>当可用磁盘空间已满时,配额管理器将根据LRU策略开始清除数据——最近最少使用的源将首先被删除,然后是下一个,直到浏览器不再超过限制。</p>
+
+<p>我们使用临时存储跟踪每个源的“上次访问时间”。 一旦达到临时存储的全局限制(之后会有更多限制),我们将尝试查找所有当前未使用的源(即没有打开选项卡/应用程序的那些来保持打开的数据存储)。 然后根据“上次访问时间”对它们进行排序。 然后删除最近最少使用的源,直到有足够的空间来满足触发此源回收的请求。</p>
+
+<h2 id="参见">参见</h2>
+
+<ul>
+ <li><a href="http://www.html5rocks.com/en/tutorials/offline/quota-research/">在移动浏览器上使用配额</a>(<a href="http://blog.agektmr.com" title="Eiji Kitamura"> Eiji Kitamura</a>著):详细分析了移动浏览器上的客户端存储。</li>
+ <li><a href="https://developers.google.com/web/updates/2011/11/Quota-Management-API-Fast-Facts">配额管理API:快速实践</a> (<a href="http://blog.agektmr.com" title="Eiji Kitamura">Eiji Kitamura</a>著):查看Chrome / Blink中的配额管理API(也应包括Opera)。</li>
+</ul>
diff --git a/files/zh-cn/web/api/indexeddb_api/checking_when_a_deadline_is_due/index.html b/files/zh-cn/web/api/indexeddb_api/checking_when_a_deadline_is_due/index.html
new file mode 100644
index 0000000000..e1025c000a
--- /dev/null
+++ b/files/zh-cn/web/api/indexeddb_api/checking_when_a_deadline_is_due/index.html
@@ -0,0 +1,208 @@
+---
+title: Checking when a deadline is due
+slug: Web/API/IndexedDB_API/Checking_when_a_deadline_is_due
+translation_of: Web/API/IndexedDB_API/Checking_when_a_deadline_is_due
+---
+<div>{{DefaultAPISidebar("IndexedDB")}}</div>
+
+<div class="summary">
+<p>在本文中,我们将看一个复杂的示例,该示例涉及根据IndexedDB存储的截止日期检查当前时间和日期。这里的主要复杂因素是检查存储的截止日期信息(月,小时,日等)与Date对象的当前时间和日期。</p>
+</div>
+
+<p><img alt="A screenshot of the sample app. A red main title saying To do app, a test to-do item, and a red form for users to enter new tasks" src="https://mdn.mozillademos.org/files/6319/to-do-app.png" style="float: left; height: 569px; margin-bottom: 20px; margin-right: 20px; width: 320px;"></p>
+
+<p>The main example application we will be referring to in this article is <strong>To-do list notifications</strong>, a simple to-do list application that stores task titles and deadline times and dates via <a href="/en-US/docs/Web/API/IndexedDB_API">IndexedDB</a>, and then provides users with notifications when deadline dates are reached, via the <a href="/en-US/docs/Web/API/notification">Notification</a>, and <a href="/en-US/docs/Web/Guide/API/Vibration">Vibration</a> APIs. You can <a href="https://github.com/chrisdavidmills/to-do-notifications/tree/gh-pages">download the To-do list notifications app from github</a> and play around with the source code, or <a href="http://mdn.github.io/to-do-notifications/">view the app running live</a>.</p>
+
+<h2 id="基本问题">基本问题</h2>
+
+<p>在待办事项应用程序中,我们希望首先以显示时机器可读和人类可理解的格式记录时间和日期信息,然后检查每个时间和日期是否在当前时刻发生。基本上,我们想要检查现在的时间和日期,然后检查每个存储的事件,看看他们的截止日期是否与当前时间和日期相匹配。如果他们这样做,我们希望通过某种通知让用户知道。</p>
+
+<p>This would be easy if we were just comparing two {{jsxref("Global_Objects/Date", "Date")}} objects, but of course humans don't want to enter deadline information in the same format JavaScript understands. Human-readable dates are quite different, with a number of different representations.</p>
+
+<h3 id="Recording_the_date_information">Recording the date information</h3>
+
+<p>为了在移动设备上提供合理的用户体验,并减少歧义,我决定创建一个HTML表单:</p>
+
+<p><img alt="The form of the to-do app, containing fields to fill in a task title, and minute, hour, day, month and year values for the deadline." src="https://mdn.mozillademos.org/files/6321/to-do-app-form2.png" style="float: left; height: 311px; margin-bottom: 20px; margin-right: 40px; width: 288px;"></p>
+
+<ul>
+ <li>用于输入待办事项列表标题的文本输入。这是用户输入中最不可避免的一点。</li>
+ <li>Number inputs for the hour and minute parts of the deadline. On browsers that support <code>type="number"</code>, you get a nice little up and down arrow number picker. On mobile platforms you tend to get a numeric keypad for entering data, which is helpful. On others you just get a standard text input, which is okay.</li>
+ <li>{{HTMLElement("select")}} 是用于输入截止日期的日期,月份和年份的元素。因为这些值对于用户来说是最模糊的(7,星期日,太阳?04,4,4月,4月?2013,'13,13?),我认为最好的解决方案是给他们一个选择,这也为移动用户节省了恼人的打字。日期记录为月份的数字天数,月份记录为完整月份名称,年份记录为完整的四位数年份数字</li>
+</ul>
+
+<p>当我们点击submit按钮是, 将会运行函数 <code>addData()</code> , 示例:</p>
+
+<pre class="brush: js">function addData(e) {
+ e.preventDefault();
+
+ if(title.value == '' || hours.value == null || minutes.value == null || day.value == '' || month.value == '' || year.value == null) {
+ note.innerHTML += '&lt;li&gt;Data not submitted — form incomplete.&lt;/li&gt;';
+ return;
+ }
+</pre>
+
+<p>In this segment, we check to see if the form fields have all been filled in. If not, we drop a message into our developer notifications pane (see the bottom left of the app UI) to tell the user what is going on, and exit out of the function. This step is mainly for browsers that don't support HTML form validation (I have used the <code>required</code> attribute in my HTML to force validation, in those that do.)</p>
+
+<pre class="brush: js"> else {
+ var newItem = [
+ {
+ taskTitle: title.value,
+ hours : hours.value,
+ minutes : minutes.value,
+ day : day.value,
+ month : month.value,
+ year : year.value,
+ notified : "no"
+ }
+ ];
+
+ // open a read/write db transaction, ready for adding the data
+ var transaction = db.transaction(["toDoList"], "readwrite");
+
+ // report on the success of opening the transaction
+ transaction.oncomplete = function(event) {
+ note.innerHTML += '&lt;li&gt;Transaction opened for task addition.&lt;/li&gt;';
+ };
+
+ transaction.onerror = function(event) {
+ note.innerHTML += '&lt;li&gt;Transaction not opened due to error. Duplicate items not allowed.&lt;/li&gt;';
+ };
+
+ // create an object store on the transaction
+ var objectStore = transaction.objectStore("toDoList");
+
+ // add our newItem object to the object store
+ var request = objectStore.add(newItem[0]); </pre>
+
+<p>In this section we create an object called <code>newItem</code> that stores the data in the format required to insert it into the database. The next few lines open the database transaction and provide messages to notify the user if this was successful or failed.Then an <code>objectStore</code> is created into which the new item is added. The <code>notified</code> property of the data object indicates that the to-do list item's deadline has not yet come up and been notified - more on this later!</p>
+
+<div class="note">
+<p><strong>Note:</strong> The <code>db</code> variable stores a reference to the IndexedDB database instance; we can then use various properties of this variable to manipulate the data.</p>
+</div>
+
+<pre class="brush: js">
+ request.onsuccess = function(event) {
+
+ note.innerHTML += '&lt;li&gt;New item added to database.&lt;/li&gt;';
+
+ title.value = '';
+ hours.value = null;
+ minutes.value = null;
+ day.value = 01;
+ month.value = 'January';
+ year.value = 2020;
+ };
+ }</pre>
+
+<p>下一节将创建一条日志消息,说明新项目添加成功,并重置表单,以便为下一个任务输入做好准备。</p>
+
+<pre class="brush: js">
+ // update the display of data to show the newly added item, by running displayData() again.
+ displayData();
+};</pre>
+
+<p>Last of all, we run the <code>displayData()</code> function, which updates the display of data in the app to show the new task that was just entered.</p>
+
+<h3 id="Checking_whether_a_deadline_has_been_reached">Checking whether a deadline has been reached</h3>
+
+<p>At this point our data is in the database; now we want to check whether any of the the deadlines have been reached. This is done by our <code>checkDeadlines()</code> function:</p>
+
+<pre class="brush: js">function checkDeadlines() {
+ var now = new Date();</pre>
+
+<p>First we grab the current date and time by creating a blank <code>Date</code> object. Easy huh? It's about to get a bit more complex.</p>
+
+<pre class="brush: js"> var minuteCheck = now.getMinutes();
+ var hourCheck = now.getHours();
+ var dayCheck = now.getDate();
+ var monthCheck = now.getMonth();
+ var yearCheck = now.getFullYear();
+</pre>
+
+<p>The <code>Date</code> object has a number of methods to extract various parts of the date and time inside it. Here we fetch the current minutes (gives an easy numerical value), hours (gives an easy numerical value), day of the month (<code>getDate()</code> is needed for this, as <code>getDay()</code> returns the day of the week, 1-7), month (returns a number from 0-11, see below), and year (<code>getFullYear()</code> is needed; <code>getYear()</code> is deprecated, and returns a weird value that is not much use to anyone!)</p>
+
+<pre class="brush: js"> var objectStore = db.transaction(['toDoList'], "readwrite").objectStore('toDoList');
+
+ objectStore.openCursor().onsuccess = function(event) {
+ var cursor = event.target.result;
+
+ if(cursor) {</pre>
+
+<p>Next we create another IndexedDB <code>objectStore</code>, and use the <code>openCursor()</code> method to open a cursor, which is basically a way in IndexedDB to iterate through all the items in the store. We then loop through all the items in the cursor for as long as there is a valid item left in the cursor.</p>
+
+<pre class="brush: js"> switch(cursor.value.month) {
+ case "January":
+ var monthNumber = 0;
+ break;
+ case "February":
+ var monthNumber = 1;
+ break;
+
+ // other lines removed from listing for brevity
+
+ case "December":
+ var monthNumber = 11;
+ break;
+ default:
+ alert('Incorrect month entered in database.');
+ }</pre>
+
+<p>我们要做的第一件事是将我们存储在数据库中的月份名称转换为JavaScript将理解的月份号码。如前所述,JavaScript Date对象将月份值创建为0到11之间的数字。   </p>
+
+<pre class="brush: js"> if(+(cursor.value.hours) == hourCheck &amp;&amp;
+ +(cursor.value.minutes) == minuteCheck &amp;&amp;
+ +(cursor.value.day) == dayCheck &amp;&amp;
+ monthNumber == monthCheck &amp;&amp;
+ cursor.value.year == yearCheck &amp;&amp;
+ notified == "no") {
+
+ // If the numbers all do match, run the createNotification()
+ // function to create a system notification
+ createNotification(cursor.value.taskTitle);
+ }</pre>
+
+<p>With the current time and date segments that we want to check against the IndexedDB stored values all assembled, it is time to perform the checks. We want all the values to match before we show the user some kind of notification to tell them their deadline is up.</p>
+
+<p>The <code>+</code> operator in this case converts numbers with leading zeros into their non leading zero equivalents, e.g. 09 -&gt; 9. This is needed because JavaScript <code>Date</code> number values never have leading zeros, but our data might.</p>
+
+<p>The <code>notified == "no"</code> check is designed to make sure you will only get one notification per to-do item. When a notification is fired for each item object, its <code>notification</code> property is set to <code>"yes"</code> so this check will not pass on the next iteration, via the following code inside the <code>createNotification()</code> function (read <a href="/en-US/docs/Web/API/IndexedDB_API/Using_IndexedDB">Using IndexedDB</a> for an explanation):</p>
+
+<pre class="brush: js"> // now we need to update the value of notified to "yes" in this particular data object, so the
+ // notification won't be set off on it again
+
+ // first open up a tranaction as usual
+ var objectStore = db.transaction(['toDoList'], "readwrite").objectStore('toDoList');
+
+ // get the to-do list object that has this title as it's title
+ var request = objectStore.get(title);
+
+ request.onsuccess = function() {
+ // grab the data object returned as the result
+ var data = request.result;
+
+ // update the notified value in the object to "yes"
+ data.notified = "yes";
+
+ // create another request that inserts the item back into the database
+ var requestUpdate = objectStore.put(data);
+
+ // when this new request succeeds, run the displayData() function again to update the display
+ requestUpdate.onsuccess = function() {
+ displayData();
+ }</pre>
+
+<p>If the checks all match, we then run the <code>createNotification()</code> function to provide a notification to the user.</p>
+
+<pre class="brush: js"> cursor.continue();
+ }
+ }
+}</pre>
+
+<p>该函数的最后一行将光标移开,这导致上述截止日期检查机制为存储在IndexedDB中的下一个任务运行。</p>
+
+<h3 id="Keep_on_checking!">Keep on checking!</h3>
+
+<p>Of course, it is no use to just run the above deadline checking function once! We want to keep constantly checking all the deadlines to see if any of them are being reached. To do this, we are simply using <code>setInterval()</code> to run <code>checkDeadlines()</code> once per second:</p>
+
+<pre class="brush: js">setInterval(checkDeadlines, 1000);</pre>
diff --git a/files/zh-cn/web/api/indexeddb_api/index.html b/files/zh-cn/web/api/indexeddb_api/index.html
new file mode 100644
index 0000000000..ca8c7791b1
--- /dev/null
+++ b/files/zh-cn/web/api/indexeddb_api/index.html
@@ -0,0 +1,155 @@
+---
+title: IndexedDB
+slug: Web/API/IndexedDB_API
+tags:
+ - IndexedDB
+ - IndexedDB API
+ - Object Storage
+ - Workers
+ - localforage
+translation_of: Web/API/IndexedDB_API
+---
+<p>{{DefaultAPISidebar("IndexedDB")}}</p>
+
+<p>IndexedDB 是一种底层 API,用于在客户端存储大量的结构化数据(也包括文件/二进制大型对象(blobs))。该 API 使用索引实现对数据的高性能搜索。虽然 <a href="/zh-CN/docs/Web/API/Web_Storage_API" title="en-US/docs/DOM/Storage">Web Storage</a> 在存储较少量的数据很有用,但对于存储更大量的结构化数据来说力不从心。而 IndexedDB 提供了这种场景的解决方案。本页面 MDN IndexedDB 的主要引导页 - 这里,我们提供了完整的 API 参考和使用指南,浏览器支持细节,以及关键概念的一些解释的链接。</p>
+
+<p>{{AvailableInWorkers}}</p>
+
+<div class="note">
+<p><strong>注意</strong>:IndexedDB API是强大的,但对于简单的情况可能看起来太复杂。如果你更喜欢一个简单的API,请尝试  <a href="https://localforage.github.io/localForage/">localForage</a>、<a href="http://www.dexie.org/">dexie.js</a>、<a href="https://pouchdb.com/">PouchDB</a>、<a href="https://www.npmjs.com/package/idb">idb</a>、<a href="https://www.npmjs.com/package/idb-keyval">idb-keyval</a>、<a href="https://jsstore.net/">JsStore</a> 或者 <a href="https://github.com/google/lovefield">lovefield</a>  之类的库,这些库使 IndexedDB 对开发者来说更加友好。</p>
+</div>
+
+<h2 id="关键概念和用法">关键概念和用法</h2>
+
+<p>IndexedDB 是一个事务型数据库系统,类似于基于 SQL 的 RDBMS。 然而,不像 RDBMS 使用固定列表,IndexedDB 是一个基于 JavaScript 的面向对象数据库。IndexedDB 允许您存储和检索用<strong>键</strong>索引的对象;可以存储<a href="https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/The_structured_clone_algorithm">结构化克隆算法</a>支持的任何对象。您只需要指定数据库模式,打开与数据库的连接,然后检索和更新一系列<strong>事务</strong>。</p>
+
+<ul>
+ <li>阅读更多关于 <a href="https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Basic_Concepts_Behind_IndexedDB">IndexedDB背后的概念</a>。</li>
+ <li>从<a href="/zh-CN/docs/Web/API/IndexedDB_API/Using_IndexedDB">使用 IndexedDB</a> 指南的第一准则中学习异步使用 IndexedDB。</li>
+ <li>同时使用 IndexedDB 储存离线数据和 Service Workers 储存离线资源,其简述请查看 <a href="/zh-CN/docs/Web/Progressive_web_apps/Offline_Service_workers">Service Workers 制作离线 PWA</a>。</li>
+</ul>
+
+<div class="note">
+<p>注意: 正如大多数的 web 储存解决方案一样,IndexedDB 也遵守<a href="/zh-CN/docs/Web/Security/Same-origin_policy">同源策略</a>。因此当你在某个域名下操作储存数据的时候,你不能操作其他域名下的数据。</p>
+</div>
+
+<h3 id="同步和异步(Synchronous、asynchronous)">同步和异步(Synchronous、asynchronous)</h3>
+
+<p>使用 IndexedDB 执行的操作是异步执行的,以免阻塞应用程序。IndexedDB 最初包括同步和异步 API。同步 API 仅用于 <a href="https://developer.mozilla.org/en-US/docs/Web/Guide/Performance/Using_web_workers">Web Workers</a>,且已从规范中移除,因为尚不清晰是否需要。但如果 Web 开发人员有足够的需求,可以重新引入同步 API。</p>
+
+<h3 id="储存限制和回收标准">储存限制和回收标准</h3>
+
+<p>有许多 Web 技术在客户端(即本地磁盘)存储各种数据。IndexedDB 是最常见的一个。浏览器计算分配给 Web 数据存储的空间以及达到该限制时要删除的内容的过程并不简单,并且在浏览器之间有所不同。<a href="/zh-CN/docs/Web/API/IndexedDB_API/Browser_storage_limits_and_eviction_criteria">浏览器存储限制和回收标准</a>尝试解释这是如何工作的,至少在火狐的情况下是如此。</p>
+
+<h2 id="接口">接口</h2>
+
+<p>为了获取数据库的访问权限,需要在 <a href="/zh-CN/docs/Web/DOM/Window">window</a> 对象的 <a href="/zh-CN/docs/Web/API/IDBEnvironment.indexedDB">indexedDB</a> 属性上调用 <a href="/zh-CN/docs/Web/API/IDBFactory.open">open()</a> 方法。该方法返回一个 {{domxref("IDBRequest")}} 对象;异步操作通过在 {{domxref("IDBRequest")}} 对象上触发事件来和调用程序进行通信。</p>
+
+<h3 id="连接数据库">连接数据库</h3>
+
+<dl>
+ <dt>{{domxref("IDBEnvironment")}}</dt>
+ <dd>提供 IndexedDB 功能。它由 {{domxref("window")}} 和 {{domxref("worker")}} 实现,这个接口不再是 2.0 规范的一部分。</dd>
+ <dt>{{domxref("IDBFactory")}}</dt>
+ <dd>提供数据库访问。这是全局对象 {{domxref("WindowOrWorkerGlobalScope/indexedDB", "indexedDB")}} 实现的接口,因此是 API 的入口。</dd>
+ <dt>{{domxref("IDBOpenDBRequest")}}</dt>
+ <dd>表示一个打开数据库的请求。</dd>
+ <dt>{{domxref("IDBDatabase")}}</dt>
+ <dd>表示一个数据库连接。这是在数据库中获取事务的唯一方式。</dd>
+ <dt>
+ <h3 id="接收和修改数据">接收和修改数据</h3>
+ </dt>
+ <dt>{{domxref("IDBTransaction")}}</dt>
+ <dd>表示一个事务。在数据库上创建一个事务,指定作用域(例如要访问的存储对象),并确定所需的访问类型(只读或读写)。</dd>
+ <dt>{{domxref("IDBRequest")}}</dt>
+ <dd>处理数据库请求并提供对结果访问的通用接口。</dd>
+ <dt>{{domxref("IDBObjectStore")}}</dt>
+ <dd>表示允许访问通过主键查找的 IndexedDB 数据库中的一组数据的对象存储区。</dd>
+ <dt>{{domxref("IDBIndex")}}</dt>
+ <dd><font><font>也是为了允许访问 IndexedDB 数据库中的数据子集,但使用索引来检索记录而不是主键。</font><font>这有时比使用 </font></font><a href="/zh-CN/docs/Web/API/IDBObjectStore">IDBObjectStore</a> 更快<font><font>。</font></font></dd>
+ <dt>{{domxref("IDBCursor")}}</dt>
+ <dd>迭代对象存储和索引。</dd>
+ <dt>{{domxref("IDBCursorWithValue")}}</dt>
+ <dd>迭代对象存储和索引并返回游标的当前值。</dd>
+ <dt>{{domxref("IDBKeyRange")}}</dt>
+ <dd>定义可用于从特定范围内的数据库检索数据的键范围。</dd>
+ <dt>{{domxref("IDBLocaleAwareKeyRange")}} {{Non-standard_inline}}</dt>
+ <dd>定义一个键范围,可用于从特定范围内的数据库中检索数据,并根据为特定索引指定的语言环境的规则进行排序(详见 <a href="/zh-CN/docs/Web/API/IDBObjectStore/createIndex#Parameters">createIndex()</a> 的参数)。这个接口不再是 2.0 规范的一部分。</dd>
+ <dt>
+ <h3 id="自定义事件接口">自定义事件接口</h3>
+
+ <p>此规范使用以下自定义接口触发事件:</p>
+ </dt>
+ <dt>{{domxref("IDBVersionChangeEvent")}}</dt>
+ <dd>作为 {{domxref("IDBOpenDBRequest.onupgradeneeded")}} 事件的处理程序的结果,<code>IDBVersionChangeEvent </code>接口表示数据库的版本已经发生了改变。</dd>
+ <dt>
+ <h3 id="过时的接口">过时的接口</h3>
+
+ <p><font>规范的早期版本还定义了这些现在已删除的接口。这些文档便于您需要更新以前编写的代码</font>:</p>
+ </dt>
+ <dt>{{domxref("IDBVersionChangeRequest")}} {{obsolete_inline}}</dt>
+ <dd><font>表示更改数据库版本的请求。改变数据库版本的方法已经改变了(通过调用</font>{{domxref("IDBFactory.open")}} 而非{{domxref("IDBDatabase.setVersion")}}),接口{{domxref("IDBOpenDBRequest")}} 现在拥有{{domxref("IDBVersionChangeRequest")}}。</dd>
+ <dt>{{domxref("IDBDatabaseException")}}  {{obsolete_inline}}</dt>
+ <dd>表示执行数据库操作时可能遇到的异常情况。</dd>
+ <dt>{{domxref("IDBTransactionSync")}} {{obsolete_inline}}</dt>
+ <dd>同步版本的 {{domxref("IDBTransaction")}}。</dd>
+ <dt>{{domxref("IDBObjectStoreSync")}} {{obsolete_inline}}</dt>
+ <dd>同步版本的 {{domxref("IDBObjectStore")}}。</dd>
+ <dt>{{domxref("IDBIndexSync")}} {{obsolete_inline}}</dt>
+ <dd>同步版本的 {{domxref("IDBIndex")}}。</dd>
+ <dt>{{domxref("IDBFactorySync")}} {{obsolete_inline}}</dt>
+ <dd>同步版本的 {{domxref("IDBFactory")}}。</dd>
+ <dt>{{domxref("IDBEnvironmentSync")}} {{obsolete_inline}}</dt>
+ <dd>同步版本的 {{domxref("IDBEnvironment")}}。</dd>
+ <dt>{{domxref("IDBDatabaseSync")}} {{obsolete_inline}}</dt>
+ <dd>同步版本的 {{domxref("IDBDatabase")}}。</dd>
+ <dt>{{domxref("IDBCursorSync")}} {{obsolete_inline}}</dt>
+ <dd>同步版本的 {{domxref("IDBCursor")}}。</dd>
+</dl>
+
+<h2 id="示例">示例</h2>
+
+<ul>
+ <li><a href="https://marco-c.github.io/eLibri/">eLibri:</a> 由 IndexedDB Mozilla DevDerby 的获奖者 Marco Castelluccio 编写的一个功能强大的图书馆和电子书阅读器应用。</li>
+ <li><a href="https://github.com/chrisdavidmills/to-do-notifications/tree/gh-pages">To-do Notifications</a> (<a href="https://mdn.github.io/to-do-notifications/">view example live</a>): 参考文档中示例的应用程序。</li>
+ <li><a href="http://hacks.mozilla.org/2012/02/storing-images-and-files-in-indexeddb/">Storing images and files in IndexedDB</a></li>
+</ul>
+
+<h2 id="Browser_compatibility" name="Browser_compatibility">规范</h2>
+
+<table>
+ <thead>
+ <tr>
+ <th scope="col">Specification</th>
+ <th scope="col">Status</th>
+ <th scope="col">Comment</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>{{SpecName("IndexedDB 2")}}</td>
+ <td>{{Spec2("IndexedDB 2")}}</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>{{SpecName('IndexedDB')}}</td>
+ <td>{{Spec2('IndexedDB')}}</td>
+ <td>Initial definition</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="参见">参见</h2>
+
+<ul>
+ <li><a href="https://localforage.github.io/localForage/">localForage</a>:一个简单的 Polyfill,提供了简单的客户端数据存储的值语法。它在后台使用 IndexedDB,并在不支持 IndexedDB 的浏览器中回退到   WebSQL 或 localStorage。</li>
+ <li><a href="http://www.dexie.org/">Dexie.js</a>:IndexedDB 的包装,通过简单的语法,可以更快地进行代码开发。</li>
+ <li><a href="https://github.com/erikolson186/zangodb">ZangoDB</a>:类似 MongoDB 的 IndexedDB 接口,支持 MongoDB 的大多数熟悉的过滤、投影、排序、更新和聚合功能。</li>
+ <li><a href="http://jsstore.net/">JsStore</a>:一个带有 SQL 语法的 IndexedDB 包装器。</li>
+ <li><a href="https://github.com/mWater/minimongo">MiniMongo</a>:<font>由 localstorage 支持的客户端内存中的 mongodb,通过 http 进行服务器同步。</font><font>MeteorJS 使用 MiniMongo。</font></li>
+ <li><a href="https://pouchdb.com/">PouchDB</a>:使用 IndexedDB 在浏览器中实现 CouchDB 的客户端。</li>
+ <li><a href="https://www.npmjs.com/package/idb">idb</a>:一个微小的(〜1.15k)库,大多 API 与 IndexedDB 类似,但做了一些小的改进,让数据库的可用性得到了大大的提升。</li>
+ <li><a href="https://www.npmjs.com/package/idb-keyval">idb-keyval</a>:使用 IndexedDB 实现的超级简单且小巧的(~600B)基于 Promise 的键值对存储。</li>
+ <li><a href="https://www.npmjs.com/package/@sifrr/storage">sifrr-storage</a>:一个非常小的(~2kB)基于 Promise 的客户端键值数据库。基于 IndexedDB、localStorage、WebSQL 和 Cookies 实现。它可以自动选择上述支持的数据库,并按照优先顺序使用。</li>
+ <li><a href="https://github.com/google/lovefield">lovefield</a>:Lovefield 是一个用于 Web App 的关系型数据库,使用 JavaScript 编写,可以在不同的浏览器环境中运行,提供了类似 SQL 的 API,速度快、安全且易用。</li>
+</ul>
diff --git a/files/zh-cn/web/api/indexeddb_api/using_indexeddb/index.html b/files/zh-cn/web/api/indexeddb_api/using_indexeddb/index.html
new file mode 100644
index 0000000000..863c4a3ad1
--- /dev/null
+++ b/files/zh-cn/web/api/indexeddb_api/using_indexeddb/index.html
@@ -0,0 +1,1340 @@
+---
+title: 使用 IndexedDB
+slug: Web/API/IndexedDB_API/Using_IndexedDB
+tags:
+ - IndexedDB
+ - 中文
+ - 入门
+ - 教程
+ - 文档
+translation_of: Web/API/IndexedDB_API/Using_IndexedDB
+---
+<p>{{DefaultAPISidebar("IndexedDB")}}</p>
+
+<p class="summary">IndexedDB 是一种可以让你在用户的浏览器内持久化存储数据的方法。IndexedDB 为生成 Web Application 提供了丰富的查询能力,使我们的应用在在线和离线时都可以正常工作。</p>
+
+<h2 id="关于本文档">关于本文档</h2>
+
+<p>本篇教程将教会你如何使用 IndexedDB 的异步 API。如果你对 IndexedDB 还不熟悉,你应该首先阅读<a href="https://developer.mozilla.org/en/IndexedDB/Basic_Concepts_Behind_IndexedDB" title="https://developer.mozilla.org/en/IndexedDB/Basic_Concepts_Behind_IndexedDB">有关 IndexedDB 的基本概念</a>。</p>
+
+<p>有关 IndexedDB API 的参考手册,请参见 <a href="https://developer.mozilla.org/en/IndexedDB" title="https://developer.mozilla.org/en/IndexedDB">IndexedDB</a> 这篇文章及其子页面,包括 IndexedDB 使用的对象类型,以及异步 API(同步 API 已从规范中删除)。</p>
+
+<h2 id="pattern" name="pattern">基本模式</h2>
+
+<p>IndexedDB 鼓励使用的基本模式如下所示:</p>
+
+<ol>
+ <li>打开数据库。</li>
+ <li>在数据库中创建一个对象仓库(object store)。</li>
+ <li>启动一个事务,并发送一个请求来执行一些数据库操作,像增加或提取数据等。</li>
+ <li>通过监听正确类型的 DOM 事件以等待操作完成。</li>
+ <li>在操作结果上进行一些操作(可以在 request 对象中找到)</li>
+</ol>
+
+<p>有了这些提纲,我们可以进行更具体的探讨。</p>
+
+<h2 id="生成和构建一个对象存储空间">生成和构建一个对象存储空间</h2>
+
+<p>由于 IndexedDB 本身的规范还在持续演进中,当前的 IndexedDB 的实现还是使用浏览器前缀。在规范更加稳定之前,浏览器厂商对于标准 IndexedDB API 可能都会有不同的实现。但是一旦大家对规范达成共识的话,厂商就会不带前缀标记地进行实现。实际上一些实现已经移除了浏览器前缀:IE 10,Firefox 16 和 Chrome 24。当使用前缀的时候,基于 Gecko 内核的浏览器使用 <code>moz</code> 前缀,基于 WebKit 内核的浏览器会使用 <code>webkit</code> 前缀。</p>
+
+<h3 id="使用实验版本的_IndexedDB">使用实验版本的 IndexedDB</h3>
+
+<p>如果你希望在仍旧使用前缀的浏览器中测试你的代码, 可以使用下列代码:  </p>
+
+<pre class="brush: js">// 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>
+
+<p>要注意的是使用前缀的实现可能会有问题,或者是实现的并不完整,也可能遵循的还是旧版的规范。因此不建议在生产环境中使用。我们更倾向于明确的不支持某一浏览器,而不是声称支持但是实际运行中却出问题:</p>
+
+<pre class="brush: js">if (!window.indexedDB) {
+ window.alert("Your browser doesn't support a stable version of IndexedDB. Such and such feature will not be available.")
+}
+</pre>
+
+<h3 id="打开数据库">打开数据库</h3>
+
+<p>我们像下面这样开始整个过程:</p>
+
+<pre class="brush: js">// 打开我们的数据库
+var request = window.indexedDB.open("MyTestDatabase");
+</pre>
+
+<p>看到了吗? 打开数据库就像任何其他操作一样 — 你必须进行 "request"。</p>
+
+<p>open 请求不会立即打开数据库或者开始一个事务。 对 <code>open()</code> 函数的调用会返回一个我们可以作为事件来处理的包含 result(成功的话)或者错误值的 <a href="/en-US/docs/IndexedDB/IDBOpenDBRequest" title="/en-US/docs/IndexedDB/IDBOpenDBRequest"><code>IDBOpenDBRequest</code></a> 对象。在 IndexedDB 中的大部分异步方法做的都是同样的事情 - 返回一个包含 result 或错误的 <a href="/en-US/docs/IndexedDB/IDBRequest" title="/en-US/docs/IndexedDB/IDBRequest"><code style="font-size: 14px; color: rgb(51, 51, 51);">IDBRequest</code></a> 对象。open 函数的结果是一个 <code style="font-size: 14px; color: rgb(51, 51, 51);"><a href="/en-US/docs/IndexedDB/IDBDatabase" title="/en-US/docs/IndexedDB/IDBDatabase">IDBDatabase</a></code> 对象的实例。</p>
+
+<p>该 open 方法接受第二个参数,就是数据库的版本号。数据库的版本决定了数据库架构,即数据库的对象仓库(object store)和他的结构。如果数据库不存在,<code>open</code> 操作会创建该数据库,然后 <code>onupgradeneeded</code> 事件被触发,你需要在该事件的处理函数中创建数据库模式。如果数据库已经存在,但你指定了一个更高的数据库版本,会直接触发 <code>onupgradeneeded</code> 事件,允许你在处理函数中更新数据库模式。我们在后面的<a href="#Updating_the_version_of_the_database">更新数据库的版本号</a>和 {{ domxref("IDBFactory.open") }} 中会提到更多有关这方面的内容。</p>
+
+<div class="warning">
+<p>重要的:版本号是一个 unsigned long long 数字,这意味着它可以是一个特别大的数字,但不能使用浮点数,否则它将会被转变成离它最近的整数,这可能导致 <code>upgradeneeded</code> 事件不会被触发。例如,不要使用 2.4 作为版本号。<br>
+ <code>var request = indexedDB.open("MyTestDatabase", 2.4); // 不要这么做,因为版本会被置为 2。</code></p>
+</div>
+
+<h4 id="生成处理函数">生成处理函数</h4>
+
+<p>几乎所有我们产生的请求我们在处理的时候首先要做的就是添加成功和失败处理函数:</p>
+
+<pre class="brush: js">request.onerror = function(event) {
+ // Do something with request.errorCode!
+};
+request.onsuccess = function(event) {
+ // Do something with request.result!
+};</pre>
+
+<p><code>onsuccess()</code> 和 <code>onerror()</code> 这两个函数哪个被调用呢?如果一切顺利的话,一个 success 事件(即一个 type 属性被设置成<code> "success"</code> 的 DOM 事件)会被触发,<code>request</code> 会作为它的 <code>target</code>。 一旦它被触发的话,相关 <code>request</code> 的 <code>onsuccess()</code> 处理函数就会被触发,使用 success 事件作为它的参数。 否则,如果不是所有事情都成功的话,一个 error 事件(即<code> type</code> 属性被设置成 <code>"error"</code> 的 DOM 事件) 会在 request 上被触发。这将会触发使用 error 事件作为参数的 <code><code>onerror()</code></code> 方法。</p>
+
+<p>IndexedDB 的 API 被设计来尽可能地减少对错误处理的需求,所以你可能不会看到有很多的错误事件(起码,不会在你已经习惯了这些 API 之后!)。然而在打开数据库的情况下,还是有一些会产生错误事件的常见情况。最有可能出现的问题是用户决定不允许你的 web app 访问以创建一个数据库。IndexedDB 的主要设计目标之一就是允许大量数据可以被存储以供离线使用。(要了解关于针对每个浏览器你可以有多少存储空间的更多内容,请参见 <a href="/en/IndexedDB#Storage_limits" title="https://developer.mozilla.org/en/IndexedDB#Storage_limits">存储限制</a>)。  </p>
+
+<p>显然,浏览器不希望允许某些广告网络或恶意网站来污染你的计算机,所以浏览器会在任意给定的 web app 首次尝试打开一个 IndexedDB 存储时对用户进行提醒。用户可以选择允许访问或者拒绝访问。还有,IndexedDB 在浏览器的隐私模式(Firefox 的 Private Browsing 模式和 Chrome 的 Incognito 模式)下是被完全禁止的。 隐私浏览的全部要点在于不留下任何足迹,所以在这种模式下打开数据库的尝试就失败了。</p>
+
+<p>现在,假设用户已经允许了你的要创建一个数据库的请求,同时你也已经收到了一个来触发 success 回调的 success 事件;然后呢?这里的 request 是通过调用 <code>indexedDB.open() </code>产生的, 所以 <code>request.result</code> 是一个 <code>IDBDatabase</code> 的实例,而且你肯定希望把它保存下来以供后面使用。你的代码看起来可能像这样:</p>
+
+<pre class="brush: js">var db;
+var request = indexedDB.open("MyTestDatabase");
+request.onerror = function(event) {
+ alert("Why didn't you allow my web app to use IndexedDB?!");
+};
+request.onsuccess = function(event) {
+ db = event.target.result;
+};
+</pre>
+
+<h4 id="错误处理">错误处理</h4>
+
+<p>如上文所述,错误事件遵循冒泡机制。错误事件都是针对产生这些错误的请求的,然后事件冒泡到事务,然后最终到达数据库对象。如果你希望避免为所有的请求都增加错误处理程序,你可以替代性的仅对数据库对象添加一个错误处理程序,像这样:</p>
+
+<pre class="brush: js">db.onerror = function(event) {
+ // Generic error handler for all errors targeted at this database's
+ // requests!
+ alert("Database error: " + event.target.errorCode);
+};
+</pre>
+
+<p>在打开数据库时常见的可能出现的错误之一是 <code>VER_ERR</code>。这表明存储在磁盘上的数据库的版本高于你试图打开的版本。这是一种必须要被错误处理程序处理的一种出错情况。</p>
+
+<h3 id="创建和更新数据库版本号">创建和更新数据库版本号</h3>
+
+<p>当你创建一个新的数据库或者增加已存在的数据库的版本号(当{{ anch("打开数据库")}}时,指定一个比之前更大的版本号), <code>onupgradeneeded</code> 事件会被触发,<a href="https://developer.mozilla.org/en-US/docs/Web/API/IDBVersionChangeEvent">IDBVersionChangeEvent</a> 对象会作为参数传递给绑定在 <code>request.result</code>(例如例子中的 <code>db</code>)上的 <code>onversionchange </code>事件处理函数,你应该在此创建该版本需要的对象仓库(object store)。</p>
+
+<p>要更新数据库的 schema,也就是创建或者删除对象存储空间,需要实现<span style="line-height: 21px;"> </span><code style="font-size: 14px; color: rgb(51, 51, 51);">onupgradeneeded</code><span style="line-height: 21px;"> 处理程序,这个处理程序将会作为一个允许你处理对象存储空间的 </span><code style="font-size: 14px; color: rgb(51, 51, 51);">versionchange</code><span style="line-height: 21px;"> 事务的一部分被调用。</span></p>
+
+<pre class="brush: js">// 该事件仅在较新的浏览器中实现了
+request.onupgradeneeded = function(event) {
+ // 保存 IDBDataBase 接口
+ var db = event.target.result;
+
+ // 为该数据库创建一个对象仓库
+ var objectStore = db.createObjectStore("name", { keyPath: "myKey" });
+};</pre>
+
+<p>在这种情况下,数据库会保留之前版本数据库的对象仓库(object store),因此你不必再次创建这些对象仓库。你需要创建新的对象仓库,或删除不再需要的上一版本中的对象仓库。如果你需要修改一个已存在的对象仓库(例如要修改 <code>keyPath</code>),你必须先删除原先的对象仓库然后使用新的设置创建。(注意,这样会丢失对象仓库里的数据,如果你需要保存这些信息,你要在数据库版本更新前读取出来并保存在别处)。</p>
+
+<p>尝试创建一个与已存在的对象仓库重名(或删除一个不存在的对象仓库)会抛出错误。</p>
+
+<p>如果 <code>onupgradeneeded </code>事件成功执行完成,打开数据库请求的 <code>onsuccess</code> 处理函数会被触发。</p>
+
+<p>WebKit/Blink 支持当前版本的规范,同时 Chrome 23+ 、Opera 17+ 以及 IE 10+同样支持。其他和更旧的实现没有实现当前版本的规范,因此还不支持 <code>indexedDB.open(name, version).onupgradeneeded</code> 签名。有关如何在较旧 Webkit/Blink 上升级数据库版本的更多信息,请参见 <a href="https://developer.mozilla.org/en/IndexedDB/IDBDatabase#setVersion()_.0A.0ADeprecated" title="https://developer.mozilla.org/en/IndexedDB/IDBDatabase#setVersion()_.0A.0ADeprecated">IDBDatabase 参考文档</a>。</p>
+
+<h3 id="构建数据库">构建数据库</h3>
+
+<p>现在来构建数据库。IndexedDB 使用对象存仓库而不是表,并且一个单独的数据库可以包含任意数量的对象存储空间。每当一个值被存储进一个对象存储空间时,它会被和一个键相关联。键的提供可以有几种不同的方法,这取决于对象存储空间是使用 <a href="/en/IndexedDB#gloss_key_path" title="https://developer.mozilla.org/en/IndexedDB#gloss_key_path">key path</a> 还是 <a href="/en/IndexedDB#gloss_key_generator" title="en/IndexedDB#gloss key generator">key generator</a>。</p>
+
+<p>下面的表格显示了几种不同的提供键的方法。 </p>
+
+<table class="standard-table">
+ <thead>
+ <tr>
+ <th scope="col">
+ <p>键路径<br>
+ (<code>keyPath</code>)</p>
+ </th>
+ <th scope="col">
+ <p>键生成器(<code>autoIncrement</code>)</p>
+ </th>
+ <th scope="col">描述</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>No</td>
+ <td>No</td>
+ <td>这种对象存储空间可以持有任意类型的值,甚至是像数字和字符串这种基本数据类型的值。每当我们想要增加一个新值的时候,必须提供一个单独的键参数。</td>
+ </tr>
+ <tr>
+ <td>Yes</td>
+ <td>No</td>
+ <td>这种对象存储空间只能持有 JavaScript 对象。这些对象必须具有一个和 key path 同名的属性。</td>
+ </tr>
+ <tr>
+ <td>No</td>
+ <td>Yes</td>
+ <td>这种对象存储空间可以持有任意类型的值。键会为我们自动生成,或者如果你想要使用一个特定键的话你可以提供一个单独的键参数。</td>
+ </tr>
+ <tr>
+ <td>Yes</td>
+ <td>Yes</td>
+ <td>这种对象存储空间只能持有 JavaScript 对象。通常一个键被生成的同时,生成的键的值被存储在对象中的一个和 key path 同名的属性中。然而,如果这样的一个属性已经存在的话,这个属性的值被用作键而不会生成一个新的键。</td>
+ </tr>
+ </tbody>
+</table>
+
+<p>你也可以使用对象存储空间持有的对象,不是基本数据类型,在任何对象存储空间上创建索引。索引可以让你使用被存储的对象的属性的值来查找存储在对象存储空间的值,而不是用对象的键来查找。</p>
+
+<p>此外,索引具有对存储的数据执行简单限制的能力。通过在创建索引时设置 unique 标记,索引可以确保不会有两个具有同样索引 key path 值的对象被储存。因此,举例来说,如果你有一个用于持有一组 people 的对象存储空间,并且你想要确保不会有两个拥有同样 email 地址的 people,你可以使用一个带有 unique 标识的索引来确保这些。</p>
+
+<p>这听起来可能有点混乱,但下面这个简单的例子应该可以解释这些概念。首先,我们定义一些将在例子中用到的客户数据。</p>
+
+<pre class="brush: js">// 我们的客户数据看起来像这样。
+const customerData = [
+ { ssn: "444-44-4444", name: "Bill", age: 35, email: "bill@company.com" },
+ { ssn: "555-55-5555", name: "Donna", age: 32, email: "donna@home.org" }
+];</pre>
+
+<p>当然,你不会使用人们的社会保险号(ssn)作为客户表的主键,因为不是每个人都拥有社会保险号,并且你应该存储他们的生日而不是年龄。为了方便,这里我们忽略这些不合理的设计,继续往下看。</p>
+
+<p>现在让我们看看如何创建一个 IndexedDB 来存储上面的数据:</p>
+
+<pre class="brush: js">const dbName = "the_name";
+
+var request = indexedDB.open(dbName, 2);
+
+request.onerror = function(event) {
+ // 错误处理
+};
+request.onupgradeneeded = function(event) {
+ var db = event.target.result;
+
+ // 建立一个对象仓库来存储我们客户的相关信息,我们选择 ssn 作为键路径(key path)
+ // 因为 ssn 可以保证是不重复的
+ var objectStore = db.createObjectStore("customers", { keyPath: "ssn" });
+
+ // 建立一个索引来通过姓名来搜索客户。名字可能会重复,所以我们不能使用 unique 索引
+ objectStore.createIndex("name", "name", { unique: false });
+
+ // 使用邮箱建立索引,我们向确保客户的邮箱不会重复,所以我们使用 unique 索引
+ objectStore.createIndex("email", "email", { unique: true });
+
+ // 使用事务的 oncomplete 事件确保在插入数据前对象仓库已经创建完毕
+ objectStore.transaction.oncomplete = function(event) {
+ // 将数据保存到新创建的对象仓库
+ var customerObjectStore = db.transaction("customers", "readwrite").objectStore("customers");
+ customerData.forEach(function(customer) {
+ customerObjectStore.add(customer);
+ });
+ };
+};</pre>
+
+<p class="brush: js"></p>
+
+<p>正如前面提到的,<code>onupgradeneeded</code> 是我们唯一可以修改数据库结构的地方。在这里面,我们可以创建和删除对象存储空间以及构建和删除索引。</p>
+
+<p>对象仓库仅调用 <code>createObjectStore()</code> 就可以创建。这个方法使用仓库的名称,和一个参数对象。即便这个参数对象是可选的,它还是非常重要的,因为它可以让你定义重要的可选属性,并完善你希望创建的对象存储空间的类型。在我们的示例中,我们创建了一个名为“customers” 的对象仓库并且定义了一个使得每个仓库中每个对象都独一无二的 <code>keyPath</code> 。在这个示例中的属性是 “ssn”,因为社会安全号码被确保是唯一的。被存储在该仓库中的所有对象都必须存在“ssn”。</p>
+
+<p>我们也请求了一个名为 “name” 的着眼于存储的对象的 <code>name</code> 属性的索引。如同 <code>createObjectStore()</code>,<code>createIndex()</code> 提供了一个可选地 <code>options</code> 对象,该对象细化了我们希望创建的索引类型。新增一个不带 <code>name</code> 属性的对象也会成功,但是这个对象不会出现在 "name" 索引中。</p>
+
+<p>我们现在可以使用存储的用户对象的 <code>ssn</code> 直接从对象存储空间中把它们提取出来,或者通过使用索引来使用他们的 name 进行提取。要了解这些是如何实现的,请参见 <a href="/en/IndexedDB/Using_IndexedDB#Using_an_index" title="Using IndexedDB#Using an index">使用索引</a> 章节。</p>
+
+<h3 id="使用键生成器">使用键生成器</h3>
+
+<p>在创建对象仓库时设置 <code>autoIncrement</code> 标记会为该仓库开启键生成器。默认该设置是不开启的。</p>
+
+<p>使用键生成器,当你向对象仓库新增记录时键会自动生成。对象仓库生成的键往往从 1 开始,然后自动生成的新的键会在之前的键的基础上加 1。生成的键的值从来不会减小,除非数据库操作结果被回滚,比如,数据库事务被中断。因此删除一条记录,甚至清空对象仓库里的所有记录都不会影响对象仓库的键生成器。</p>
+
+<p>我们可以使用键生成器创建一个对象仓库:</p>
+
+<pre class="brush: js">// 打开 indexedDB.
+var request = indexedDB.open(dbName, 3);
+
+request.onupgradeneeded = function (event) {
+
+ var db = event.target.result;
+
+ // 设置 autoIncrement 标志为 true 来创建一个名为 names 的对象仓库
+ var objStore = db.createObjectStore("names", { autoIncrement : true });
+
+ // 因为 names 对象仓库拥有键生成器,所以它的键会自动生成。
+ // 被插入的数据可以表示如下:
+ // key : 1 =&gt; value : "Bill"
+ // key : 2 =&gt; value : "Donna"
+ customerData.forEach(function(customer) {
+ objStore.add(customer.name);
+ });
+};</pre>
+
+<p>更多关于键生成器的细节,请查阅  <a href="http://www.w3.org/TR/IndexedDB/#key-generator-concept">"W3C Key Generators"</a>。</p>
+
+<h2 id="增加、读取和删除数据">增加、读取和删除数据</h2>
+
+<p>你需要开启一个事务才能对你的创建的数据库进行操作。事务来自于数据库对象,而且你必须指定你想让这个事务跨越哪些对象仓库。一旦你处于一个事务中,你就可以目标对象仓库发出请求。你要决定是对数据库进行更改还是只需从中读取数据。事务提供了三种模式:<code>readonly</code>、<code>readwrite</code> 和 <code>versionchange</code>。</p>
+
+<p>想要修改数据库模式或结构——包括新建或删除对象仓库或索引,只能在 <code>versionchange</code> 事务中才能实现。该事务由一个指定了 version 的 {{domxref("IDBFactory.open")}} 方法启动。(在仍未实现最新标准的 WebKit 浏览器 ,{{domxref("IDBFactory.open")}} 方法只接受一个参数,即数据库的 <code>name</code>,这样你必须调用 {{domxref("IDBVersionChangeRequest.setVersion")}} 来建立 <code>versionchange</code> 事务。</p>
+
+<p>使用 <code>readonly</code> 或 <code>readwrite</code> 模式都可以从已存在的对象仓库里读取记录。但只有在 <code>readwrite</code> 事务中才能修改对象仓库。你需要使用 {{domxref("IDBDatabase.transaction")}} 启动一个事务。该方法接受两个参数:<code>storeNames</code> (作用域,一个你想访问的对象仓库的数组),事务模式 <code>mode</code>(readonly 或 readwrite)。该方法返回一个包含 {{domxref("IDBIndex.objectStore")}} 方法的事务对象,使用 {{domxref("IDBIndex.objectStore")}} 你可以访问你的对象仓库。未指定 <code>mode</code> 时,默认为 <code>readyonly</code> 模式。</p>
+
+<div class="blockIndicator note">
+<p>从 Firfox 40 起,IndexedDB 事务放松了对持久性的保证以提高性能(参见 <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1112702" rel="noopener">Bug1112702</a>)以前在 <code>readwrite</code> 事务中,只有当所有的数据确保被写入磁盘时才会触发 {{domxref("IDBTransaction.oncomplete")}}。在 Firefox 40+ 中,当操作系统被告知去写入数据后 <code>complete</code> 事件便被触发,但此时数据可能还没有真正的写入磁盘。<code>complete</code> 事件触发因此变得更快,但这样会有极小的机会发生以下情况:如果操作系统崩溃或在数据被写入磁盘前断电,那么整个事务都将丢失。由于这种灾难事件是罕见的,大多数使用者并不需要过分担心。如果由于某些原因你必须确保数据的持久性(例如你要保存一个无法再次计算的关键数据),你可以使用实验性(非标准的)<code>readwriteflush</code> 模式来创建事务以强制 <code>complete</code> 事件在数据写入磁盘后触发(查看 {{domxref("IDBDatabase.transaction")}})。</p>
+</div>
+
+<p>你可以通过使用合适的作用域和模式来加速数据库访问,这有两个提示:</p>
+
+<ul>
+ <li>定义作用域时,只指定你用到的对象仓库。这样,你可以同时运行多个不含互相重叠作用域的事务。</li>
+ <li>只在必要时指定 readwrite 事务。你可以同时执行多个 readnoly 事务,哪怕它们的作用域有重叠;但对于在一个对象仓库上你只能运行一个 readwrite 事务。了解更多,请查看<a href="https://developer.mozilla.org/en-US/docs/IndexedDB/Basic_Concepts_Behind_IndexedDB">基本概念</a>中<a href="https://developer.mozilla.org/en-US/docs/IndexedDB/Basic_Concepts_Behind_IndexedDB#Database">事务</a>的定义。</li>
+</ul>
+
+<h3 id="向数据库中增加数据">向数据库中增加数据</h3>
+
+<p>如果你刚刚创建了一个数据库,你可能想往里面写点东西。看起来会像下面这样:</p>
+
+<pre class="brush: js">var transaction = db.transaction(["customers"], "readwrite");
+// 注意: 旧的实验性接口实现使用了常量 IDBTransaction.READ_WRITE 而不是 "readwrite"。
+// 如果你想支持这样旧版本的实现,你只要这样写就可以了:
+// var transaction = db.transaction(["customers"], IDBTransaction.READ_WRITE);</pre>
+
+<p><code>transaction()</code> 方法接受两个参数(一个是可选的)并返回一个事务对象。第一个参数是事务希望跨越的对象存储空间的列表。如果你希望事务能够跨越所有的对象存储空间你可以传入一个空数组,但请不要这样做,因为标准规定传入一个空数组会导致一个InvalidAccessError(可以<a href="https://developer.mozilla.org/zh-CN/docs/Web/API/IDBDatabase/transaction">使用</a>属性<a href="https://developer.mozilla.org/en-US/docs/Web/API/IDBDatabase/objectStoreNames">db.objectStoreNames</a>)。如果你没有为第二个参数指定任何内容,你得到的是只读事务。如果你想写入数据,你需要传入 <code>"readwrite"</code> 标识。</p>
+
+<p>现在我们已经有了一个事务,我们需要理解它的生命周期。事务和事件循环的联系非常密切。如果你创建了一个事务但是并没有使用它就返回给事件循环,那么事务将会失活。保持事务活跃的唯一方法就是在其上构建一个请求。当请求完成时你将会得到一个 DOM 事件,并且,假设请求成功了,你将会有另外一个机会在回调中来延长这个事务。如果你没有延长事务就返回到了事件循环,那么事务将会变得不活跃,依此类推。只要还有待处理的请求事务就会保持活跃。事务生命周期真的很简单但是可能需要一点时间你才能对它变得习惯。还有就是来几个例子也会有所帮助。如果你开始看到 <code>TRANSACTION_INACTIVE_ERR</code> 错误代码,那么你已经把某些事情搞乱了。</p>
+
+<p>事务接收三种不同的 DOM 事件:<code>error</code>、<code>abort</code> 和 <code>complete</code>。我们已经提及 <code>error</code> 事件是冒泡机制,所以事务会接收由它产生的所有请求所产生的错误。更微妙的一点,错误会中断它所处的事务。除非你在错误发生的第一时间就调用了 <code>stopPropagation</code> 并执行了其他操作来处理错误,不然整个事务将会回滚。这种机制迫使你考虑和处理错误场景,如果觉得细致的错误处理太繁琐,你可以在数据库上添加一个全局的错误处理。如果你在事务中没有处理一个已发生的错误或者调用 abort 方法,那么该事务会被回滚,并触发 abort 事件。另外,在所有请求完成后,事务的 complete 事件会被触发。如果你进行大量数据库操作,跟踪事务而不是具体的请求会使逻辑更加清晰。</p>
+
+<p>现在你拥有了一个事务,你需要从中取出一个对象仓库。你只能在创建事务时指定的对象仓库中取出一个对象仓库。然后你可以添加任何你需要的数据。</p>
+
+<pre class="brush: js">// 在所有数据添加完毕后的处理
+transaction.oncomplete = function(event) {
+ alert("All done!");
+};
+
+transaction.onerror = function(event) {
+ // 不要忘记错误处理!
+};
+
+var objectStore = transaction.objectStore("customers");
+customerData.forEach(function(customer) {
+ var request = objectStore.add(customer);
+ request.onsuccess = function(event) {
+ // event.target.result === customer.ssn;
+ };
+});</pre>
+
+<p>调用 call() 方法产生的请求的 result 是被添加的数据的键。所以在该例中,它应该全等于被添加对象的 ssn 属性,因为对象仓库使用 ssn 属性作为键路径(key path)。注意,add() 方法的调用时,对象仓库中不能存在相同键的对象。如果你想修改一个已存在的条目,或者你不关心该数据是否已存在,你可以使用 put() 方法,就像下面 {{ anch("Updating an entry in the database") }} 模块所展示的。</p>
+
+<h2 id="从数据库中删除数据">从数据库中删除数据</h2>
+
+<p>删除数据是非常类似的:</p>
+
+<pre class="brush: js">var request = db.transaction(["customers"], "readwrite")
+ .objectStore("customers")
+ .delete("444-44-4444");
+request.onsuccess = function(event) {
+ // 删除成功!
+};</pre>
+
+<h2 id="从数据库中获取数据">从数据库中获取数据</h2>
+
+<p>现在数据库里已经有了一些信息,你可以通过几种方法对它进行提取。首先是简单的 <code>get()</code>。你需要提供键来提取值,像这样:</p>
+
+<pre class="brush: js">var transaction = db.transaction(["customers"]);
+var objectStore = transaction.objectStore("customers");
+var request = 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);
+};</pre>
+
+<p>对于一个“简单”的提取这里的代码有点多了。下面看我们怎么把它再缩短一点,假设你在数据库的级别上来进行的错误处理:</p>
+
+<pre class="brush: js">db.transaction("customers").objectStore("customers").get("444-44-4444").onsuccess = function(event) {
+ alert("Name for SSN 444-44-4444 is " + event.target.result.name);
+};</pre>
+
+<p>看看这是怎么回事。因为这里只用到一个对象仓库,你可以只传该对象仓库的名字作为参数,而不必传一个列表。并且,你只需读取数据,所以不需要 <code>readwrite</code> 事务。不指定事务模式来调用 <code>transaction</code> 你会得到一个 <code>readonly</code> 事务。另外一个微妙的地方在于你并没有保存请求对象到变量中。因为 DOM 事件把请求作为他的目标(target),你可以使用该事件来获取 <code>result </code>属性。</p>
+
+<p>注意,你可以通过限制事务的作用域和模式来加速数据库访问。这里有两个提醒:</p>
+
+<ul>
+ <li>定义<a href="https://developer.mozilla.org/zh-CN/docs/Web/API/IndexedDB_API/Using_IndexedDB$edit#scope">作用域</a>时,只指定你用到的对象仓库。这样,你可以同时运行多个不含互相重叠作用域的事务。</li>
+ <li>只在必要时指定 readwrite 事务。你可以同时执行多个 readnoly 事务,哪怕它们的作用域有重叠;但对于在一个对象仓库上你只能运行一个 readwrite 事务。了解更多,请查看<a href="https://developer.mozilla.org/en-US/docs/IndexedDB/Basic_Concepts_Behind_IndexedDB">基本概念</a>中<a href="https://developer.mozilla.org/en-US/docs/IndexedDB/Basic_Concepts_Behind_IndexedDB#Database">事务</a>的定义。</li>
+</ul>
+
+<h3 id="更新数据库中的记录">更新数据库中的记录</h3>
+
+<p>现在我们已经去除了一些数据,修改一下并把它插回数据库的操作时非常简单的。让我们来稍微更新一下上例中的数据。</p>
+
+<pre class="brush: js">var objectStore = db.transaction(["customers"], "readwrite").objectStore("customers");
+var request = objectStore.get("444-44-4444");
+request.onerror = function(event) {
+ // 错误处理
+};
+request.onsuccess = function(event) {
+ // 获取我们想要更新的数据
+ var data = event.target.result;
+
+ // 更新你想修改的数据
+ data.age = 42;
+
+ // 把更新过的对象放回数据库
+ var requestUpdate = objectStore.put(data);
+ requestUpdate.onerror = function(event) {
+ // 错误处理
+ };
+ requestUpdate.onsuccess = function(event) {
+ // 完成,数据已更新!
+ };
+};</pre>
+
+<p>所以这里我们创建了一个 <code>objectStore</code>,并通过指定 ssn 值(<code>444-44-4444</code>)从中请求了一条客户记录。然后我们把请求的结果保存在变量 <code>data</code> 中,并更新了该对象的 <code>age </code>属性,之后创建了第二个请求(<code>requestUpdate</code>)将客户数据放回 <code>objectStore</code> 来覆盖之前的值。</p>
+
+<div class="blockIndicator note">
+<p><strong>注意:</strong>In this case we've had to specify a <code>readwrite</code> transaction because we want to write to the database, not just read from it.在这个例子中我们必须指定一个 <code>readwrite</code> 事务,因为我们想要写入一个数据库,而不仅仅是从中读取。</p>
+</div>
+
+<h2 id="使用游标">使用游标</h2>
+
+<p>使用 <code>get()</code> 要求你知道你想要检索哪一个键。如果你想要遍历对象存储空间中的所有值,那么你可以使用游标。看起来会像下面这样:</p>
+
+<pre class="brush: js">var objectStore = db.transaction("customers").objectStore("customers");
+
+objectStore.openCursor().onsuccess = function(event) {
+ var cursor = event.target.result;
+ if (cursor) {
+ alert("Name for SSN " + cursor.key + " is " + cursor.value.name);
+ cursor.continue();
+ }
+ else {
+ alert("No more entries!");
+ }
+};</pre>
+
+<p><code>openCursor()</code> 函数需要几个参数。首先,你可以使用一个 key range 对象来限制被检索的项目的范围。第二,你可以指定你希望进行迭代的方向。在上面的示例中,我们在以升序迭代所有的对象。游标成功的回调有点特别。游标对象本身是请求的 <code>result</code> (上面我们使用的是简写形式,所以是 <code>event.target.result</code>)。然后实际的 key 和 value 可以根据游标对象的 <code>key</code> 和 <code>value</code> 属性被找到。如果你想要保持继续前行,那么你必须调用游标上的 <code>continue()</code> 。当你已经到达数据的末尾时(或者没有匹配 <code>openCursor()</code> 请求的条目)你仍然会得到一个成功回调,但是 <code>result</code> 属性是 <code>undefined。</code></p>
+
+<p>使用游标的一种常见模式是提取出在一个对象存储空间中的所有对象然后把它们添加到一个数组中,像这样:</p>
+
+<pre class="brush: js">var customers = [];
+
+objectStore.openCursor().onsuccess = function(event) {
+ var cursor = event.target.result;
+ if (cursor) {
+ customers.push(cursor.value);
+ cursor.continue();
+ }
+ else {
+ alert("以获取所有客户信息: " + customers);
+ }
+};</pre>
+
+<div class="blockIndicator note">
+<p><strong>注意</strong>:可选地,你可以使用 getAll() 来处理这种情况(以及 getAllKeys())。下面的代码的效果和上例相同:</p>
+
+<p>Alternatively, you can use <code>getAll()</code> to handle this case (and <code>getAllKeys()</code>) . The following code does precisely the same thing as above:</p>
+
+<pre><code>objectStore.getAll().onsuccess = function(event) {
+ alert("Got all customers: " + event.target.result);
+};</code></pre>
+
+<p>查看游标的 <code>value</code> 属性会带来性能消耗,因为对象是被懒生成的。当你使用 <code>getAll()</code> ,浏览器必须一次创建所有的对象。如果你仅仅想检索m键,那么使用游标将比使用 <code>getAll()</code> 高效得多。当然如果你想获取一个由对象仓库中所有对象组成的数组,请使用 <code>getAll()</code>。</p>
+</div>
+
+<h3 id="使用索引">使用索引</h3>
+
+<p>使用 SSN 作为键来存储客户数据是合理的,因为 SSN 唯一地标识了一个个体(对隐私来说这是否是一个好的想法是另外一个话题,不在本文的讨论范围内)。如果你想要通过姓名来查找一个客户,那么,你将需要在数据库中迭代所有的 SSN 直到你找到正确的那个。以这种方式来查找将会非常的慢,相反你可以使用索引。</p>
+
+<pre class="brush: js">// 首先,确定你已经在 request.onupgradeneeded 中创建了索引:
+// objectStore.createIndex("name", "name");
+// 否则你将得到 DOMException。
+
+var index = objectStore.index("name");
+
+index.get("Donna").onsuccess = function(event) {
+ alert("Donna's SSN is " + event.target.result.ssn);
+};</pre>
+
+<p>“name” 游标不是唯一的,因此 <code>name</code> 被设成 <code>"Donna"</code> 的记录可能不止一条。在这种情况下,你总是得到键值最小的那个。</p>
+
+<p>如果你需要访问带有给定 <code>name</code> 的所有的记录你可以使用一个游标。你可以在索引上打开两个不同类型的游标。一个常规游标映射索引属性到对象存储空间中的对象。一个键索引映射索引属性到用来存储对象存储空间中的对象的键。不同之处被展示如下:</p>
+
+<pre class="brush: js">index.openCursor().onsuccess = function(event) {
+ var cursor = event.target.result;
+ if (cursor) {
+ // cursor.key 是一个 name, 就像 "Bill", 然后 cursor.value 是整个对象。
+ alert("Name: " + cursor.key + ", SSN: " + cursor.value.ssn + ", email: " + cursor.value.email);
+ cursor.continue();
+ }
+};
+
+index.openKeyCursor().onsuccess = function(event) {
+ var cursor = event.target.result;
+ if (cursor) {
+ // cursor.key 是一个 name, 就像 "Bill", 然后 cursor.value 是那个 SSN。
+ // 没有办法可以得到存储对象的其余部分。
+ alert("Name: " + cursor.key + ", SSN: " + cursor.value);
+ cursor.continue();
+ }
+};</pre>
+
+<h3 id="指定游标的范围和方向">指定游标的范围和方向</h3>
+
+<p>如果你想要限定你在游标中看到的值的范围,你可以使用一个 key range 对象然后把它作为第一个参数传给 <code>openCursor()</code> 或是 <code>openKeyCursor()</code>。你可以构造一个只允许一个单一 key 的 key range,或者一个具有下限或上限,或者一个既有上限也有下限。边界可以是“闭合的”(也就是说 key range 包含给定的值)或者是“开放的”(也就是说 key range 不包括给定的值)。这里是它如何工作的:</p>
+
+<pre class="brush: js">// 仅匹配 "Donna"
+var singleKeyRange = IDBKeyRange.only("Donna");
+
+// 匹配所有超过“Bill”的,包括“Bill”
+var lowerBoundKeyRange = IDBKeyRange.lowerBound("Bill");
+
+// 匹配所有超过“Bill”的,但不包括“Bill”
+var lowerBoundOpenKeyRange = IDBKeyRange.lowerBound("Bill", true);
+
+// 匹配所有不超过“Donna”的,但不包括“Donna”
+var upperBoundOpenKeyRange = IDBKeyRange.upperBound("Donna", true);
+
+// 匹配所有在“Bill”和“Donna”之间的,但不包括“Donna”
+var boundKeyRange = IDBKeyRange.bound("Bill", "Donna", false, true);
+
+// 使用其中的一个键范围,把它作为 openCursor()/openKeyCursor 的第一个参数
+index.openCursor(boundKeyRange).onsuccess = function(event) {
+ var cursor = event.target.result;
+ if (cursor) {
+ // 当匹配时进行一些操作
+ cursor.continue();
+ }
+};</pre>
+
+<p>有时候你可能想要以倒序而不是正序(所有游标的默认顺序)来遍历。切换方向是通过传递 <code>prev</code> 到 <code>openCursor()</code> 方法来实现的:</p>
+
+<pre class="brush: js">objectStore.openCursor(boundKeyRange, "prev").onsuccess = function(event) {
+ var cursor = event.target.result;
+ if (cursor) {
+ // 进行一些操作
+ cursor.continue();
+ }
+};</pre>
+
+<p>如果你只是想改变遍历的方向,而不想对结果进行筛选,你只需要给第一个参数传入 null。</p>
+
+<pre class="brush: js">objectStore.openCursor(null, "prev").onsuccess = function(event) {
+ var cursor = event.target.result;
+ if (cursor) {
+ // Do something with the entries.
+ cursor.continue();
+ }
+};</pre>
+
+<p>因为 “name” 索引不是唯一的,那就有可能存在具有相同 <code>name</code> 的多条记录。要注意的是这种情况不可能发生在对象存储空间上,因为键必须永远是唯一的。如果你想要在游标在索引迭代过程中过滤出重复的,你可以传递 <code>nextunique</code> (或 <code>prevunique</code> 如果你正在向后寻找)作为方向参数。 当 <code>nextunique</code> 或是 <code>prevunique</code> 被使用时,被返回的那个总是键最小的记录。</p>
+
+<pre class="brush: js">index.openKeyCursor(null, IDBCursor.nextunique).onsuccess = function(event) {
+ var cursor = event.target.result;
+ if (cursor) {
+ // Do something with the entries.
+ cursor.continue();
+ }
+};</pre>
+
+<p>请查看”<a href="https://developer.mozilla.org/en-US/docs/Web/API/IDBCursor?redirectlocale=en-US&amp;redirectslug=IndexedDB%2FIDBCursor#Constants">IDBCursor 常量</a>“获取合法的方向参数。</p>
+
+<h2 id="当一个_web_app_在另一个标签页中被打开时的版本变更">当一个 web app 在另一个标签页中被打开时的版本变更</h2>
+
+<p>当你的网页应用以数据库版本变更的方式发生改变时,你需要考虑,如果用户在一个标签页中打开的应用里使用了旧版本的数据库,在另一个标签页里加载新版本的数据库时会发生什么。当你使用更高的版本号调用 <code>open()</code> 方法时,其他所有打开的数据库必须显式地确认请求,你才能对数据库进行修改(<code>onblocked</code> 事件会被触发知道它们被关闭或重新加载)。这里展示了它如何工作:</p>
+
+<pre class="brush: js">var openReq = mozIndexedDB.open("MyTestDatabase", 2);
+
+openReq.onblocked = function(event) {
+ // 如果其他的一些页签加载了该数据库,在我们继续之前需要关闭它们
+ alert("请关闭其他由该站点打开的页签!");
+};
+
+openReq.onupgradeneeded = function(event) {
+ // 其他的数据已经被关闭,一切就绪
+ db.createObjectStore(/* ... */);
+ useDatabase(db);
+};
+
+openReq.onsuccess = function(event) {
+ var db = event.target.result;
+ useDatabase(db);
+ return;
+};
+
+function useDatabase(db) {
+ // 当由其他页签请求了版本变更时,确认添加了一个会被通知的事件处理程序。
+ // 这里允许其他页签来更新数据库,如果不这样做,版本升级将不会发生知道用户关闭了这些页签。
+ db.onversionchange = function(event) {
+ db.close();
+ alert("A new version of this page is ready. Please reload or close this tab!");
+ };
+
+ // 处理数据库
+}</pre>
+
+<p>你同时也应监听 <code>VersionError</code> 错误来处理这种场景:已经打开的应用的初始化代码使用过时的版本再次引导打开数据的尝试。</p>
+
+<h2 id="安全">安全</h2>
+
+<p>IndexedDB 使用同源原则,这意味着它把存储空间绑定到了创建它的站点的源(典型情况下,就是站点的域或是子域),所以它不能被任何其他源访问。</p>
+
+<p>第三方窗口内容(比如 {{htmlelement("iframe")}} 内容)可以访问它所嵌入的源的 IndexedDB 仓库,除非浏览器被设置成<a href="https://support.mozilla.org/en-US/kb/disable-third-party-cookies">从不接受第三方 cookies</a>(参见 {{bug("1147821")}})。</p>
+
+<h2 id="浏览器关闭警告">浏览器关闭警告</h2>
+
+<p>当浏览器关闭(由于用户选择关闭或退出选项),包含数据库的磁盘被意外移除,或者数据库存储的权限丢失,将发生以下问题:</p>
+
+<ol>
+ <li>受影响的数据库(在浏览器关闭的场景下,所有打开的数据库)的所有事务会以 AbortError 错误中断。该影响和在每个事务中调用 {{domxref("IDBTransaction.abort()")}} 相同。</li>
+ <li>所有的事务完成后,数据库连接就会关闭。</li>
+ <li>最终,表示数据库连接的 {{domxref("IDBDatabase")}} 对象收到一个 {{event("close")}} 事件。你可以使用 {{domxref("IDBDatabase.onclose")}} 事件句柄来监听这些事件,这样你就可以知道什么时候数据库被意外关闭了。</li>
+</ol>
+
+<p>上述的行为只在 Firefox 50、Google Chrome 31(近似的) 发行版本中支持。</p>
+
+<p>在这些版本之前的浏览器,事务会静默中断,并且 {{event("close")}} 事件不会触发,这样就无法察觉数据库的异常关闭。</p>
+
+<p>由于用户可以在任何时候关闭浏览器,因此你不能依赖于任何特定事务的完成。并且在老版本的浏览器,你甚至都无法感知它们是否顺利完成。针对这种行为这里有一些启示:</p>
+
+<p>Since the user can exit the browser at any time, this means that you cannot rely upon any particular transaction to complete, and on older browsers, you don't even get told when they don't complete. There are several implications of this behavior.</p>
+
+<p>首先,你应该始终使数据库在事务结束时处于一个稳定的状态。比如,假设你使用了一个数据库来保存一个允许用户编辑的项目列表。你通过清空对象仓库然后写入新列表来在用户编辑后保存它,这存在一个危险,那就是浏览器可能在清空数据后还没有写入数据时就关闭了,使得对象仓库变得空空如也。为了避免这种情况,你应该在同一个事务中执行清空数据和写入数据的操作。</p>
+
+<p>其次,你不应该把数据库事务绑定到卸载事件上。如果卸载事件被浏览器关闭所触发,卸载事件处理函数中的任何事务都不会完成。跨浏览器会话维护信息的直观的实现方法时在浏览器(或特定页)打开时从数据库读取它,在用户和浏览器交互式更新它,然后在浏览器(或页面)关闭时保存至数据库。然而,这并不会生效。这样一来,数据库事务会在卸载事件句柄中被创建,但由于它们时异步的,所以它们在它们执行之前就会被中断。</p>
+
+<p>实际上,这里没有办法可以确保 IndexedDB 事务可以执行完毕,即使是浏览器正常关闭的情况。参见 {{ bug(870645)}}。作为一个正常关闭通知的变通方案,你可以跟踪你的事务并添加一个 <code>beforeunload</code> 事件来提醒用户,如果此时有事务在数据库卸载时还没有完成。</p>
+
+<p>至少通过添加中断提醒和 {{domxref("IDBDatabse.onclose")}},你可以得知它何时关闭了。</p>
+
+<h2 id="地区化的排序">地区化的排序</h2>
+
+<p>Mozilla 已经在 Firefox 43+ 中实现了对 IndexedDB 数据进行地区化排序的功能。默认情况下,IndexedDB 根本不会处理国际化的字符串排序,所有的数据按照英文字母序排列。举个例子,b、á、z、a 会被如下排序:</p>
+
+<ul>
+ <li>a</li>
+ <li>b</li>
+ <li>z</li>
+ <li>á</li>
+</ul>
+
+<p>这显然不是用户想要的数据排序方式,例如 Aaron 和 Áaron 在通讯录中理应相邻地排列。如果要获取国际化的排序,需要将整个数据内容调入内存,然后由客户端 JavaScript 实现排序,显然这样做不是很高效。</p>
+
+<p>这是一个新的功能,它允许开发者在使用 {{domxref("IDBObjectStore.createIndex()")}}(查看它的参数)创建索引时指定一个地区。在这种情况下,一个游标会被用来遍历数据,如果你想指定地区性的排序,你可以使用专门的 {{domxref("IDBLocaleAwareKeyRange")}}。</p>
+
+<p>{{domxref("IDBIndex")}}  还添加了新的属性如果它已经被指定了一个地区,它们是 locale(返回被指定的地区或 null)和 isAutoLocale(如果创建索引时使用了自动的地区,即使用了平台默认的地区,则返回 true;否则返回 false)。</p>
+
+<div class="blockIndicator note">
+<p><strong>注意:</strong> 现在该特性由一个标志隐藏——在 <a href="/zh-CN/docs/">about:config</a> 中开启 <code>dom.indexedDB.experimental</code> 来启用和实验该特性。</p>
+</div>
+
+<h2 id="Full_IndexedDB_example" name="Full_IndexedDB_example">一个完整的 IndexedDB 示例</h2>
+
+<h3 id="HTML_内容">HTML 内容</h3>
+
+<pre class="brush: html">&lt;script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"&gt;&lt;/script&gt;
+
+ &lt;h1&gt;IndexedDB Demo: storing blobs, e-publication example&lt;/h1&gt;
+ &lt;div class="note"&gt;
+ &lt;p&gt;
+ Works and tested with:
+ &lt;/p&gt;
+ &lt;div id="compat"&gt;
+ &lt;/div&gt;
+ &lt;/div&gt;
+
+ &lt;div id="msg"&gt;
+ &lt;/div&gt;
+
+ &lt;form id="register-form"&gt;
+ &lt;table&gt;
+ &lt;tbody&gt;
+ &lt;tr&gt;
+ &lt;td&gt;
+ &lt;label for="pub-title" class="required"&gt;
+ Title:
+ &lt;/label&gt;
+ &lt;/td&gt;
+ &lt;td&gt;
+ &lt;input type="text" id="pub-title" name="pub-title" /&gt;
+ &lt;/td&gt;
+ &lt;/tr&gt;
+ &lt;tr&gt;
+ &lt;td&gt;
+ &lt;label for="pub-biblioid" class="required"&gt;
+ Bibliographic ID:&lt;br/&gt;
+ &lt;span class="note"&gt;(ISBN, ISSN, etc.)&lt;/span&gt;
+ &lt;/label&gt;
+ &lt;/td&gt;
+ &lt;td&gt;
+ &lt;input type="text" id="pub-biblioid" name="pub-biblioid"/&gt;
+ &lt;/td&gt;
+ &lt;/tr&gt;
+ &lt;tr&gt;
+ &lt;td&gt;
+ &lt;label for="pub-year"&gt;
+ Year:
+ &lt;/label&gt;
+ &lt;/td&gt;
+ &lt;td&gt;
+ &lt;input type="number" id="pub-year" name="pub-year" /&gt;
+ &lt;/td&gt;
+ &lt;/tr&gt;
+ &lt;/tbody&gt;
+ &lt;tbody&gt;
+ &lt;tr&gt;
+ &lt;td&gt;
+ &lt;label for="pub-file"&gt;
+ File image:
+ &lt;/label&gt;
+ &lt;/td&gt;
+ &lt;td&gt;
+ &lt;input type="file" id="pub-file"/&gt;
+ &lt;/td&gt;
+ &lt;/tr&gt;
+ &lt;tr&gt;
+ &lt;td&gt;
+ &lt;label for="pub-file-url"&gt;
+ Online-file image URL:&lt;br/&gt;
+ &lt;span class="note"&gt;(same origin URL)&lt;/span&gt;
+ &lt;/label&gt;
+ &lt;/td&gt;
+ &lt;td&gt;
+ &lt;input type="text" id="pub-file-url" name="pub-file-url"/&gt;
+ &lt;/td&gt;
+ &lt;/tr&gt;
+ &lt;/tbody&gt;
+ &lt;/table&gt;
+
+ &lt;div class="button-pane"&gt;
+ &lt;input type="button" id="add-button" value="Add Publication" /&gt;
+ &lt;input type="reset" id="register-form-reset"/&gt;
+ &lt;/div&gt;
+ &lt;/form&gt;
+
+ &lt;form id="delete-form"&gt;
+ &lt;table&gt;
+ &lt;tbody&gt;
+ &lt;tr&gt;
+ &lt;td&gt;
+ &lt;label for="pub-biblioid-to-delete"&gt;
+ Bibliographic ID:&lt;br/&gt;
+ &lt;span class="note"&gt;(ISBN, ISSN, etc.)&lt;/span&gt;
+ &lt;/label&gt;
+ &lt;/td&gt;
+ &lt;td&gt;
+ &lt;input type="text" id="pub-biblioid-to-delete"
+ name="pub-biblioid-to-delete" /&gt;
+ &lt;/td&gt;
+ &lt;/tr&gt;
+ &lt;tr&gt;
+ &lt;td&gt;
+ &lt;label for="key-to-delete"&gt;
+ Key:&lt;br/&gt;
+ &lt;span class="note"&gt;(for example 1, 2, 3, etc.)&lt;/span&gt;
+ &lt;/label&gt;
+ &lt;/td&gt;
+ &lt;td&gt;
+ &lt;input type="text" id="key-to-delete"
+ name="key-to-delete" /&gt;
+ &lt;/td&gt;
+ &lt;/tr&gt;
+ &lt;/tbody&gt;
+ &lt;/table&gt;
+ &lt;div class="button-pane"&gt;
+ &lt;input type="button" id="delete-button" value="Delete Publication" /&gt;
+ &lt;input type="button" id="clear-store-button"
+ value="Clear the whole store" class="destructive" /&gt;
+ &lt;/div&gt;
+ &lt;/form&gt;
+
+ &lt;form id="search-form"&gt;
+ &lt;div class="button-pane"&gt;
+ &lt;input type="button" id="search-list-button"
+ value="List database content" /&gt;
+ &lt;/div&gt;
+ &lt;/form&gt;
+
+ &lt;div&gt;
+ &lt;div id="pub-msg"&gt;
+ &lt;/div&gt;
+ &lt;div id="pub-viewer"&gt;
+ &lt;/div&gt;
+ &lt;ul id="pub-list"&gt;
+ &lt;/ul&gt;
+ &lt;/div&gt;</pre>
+
+<h3 id="CSS_内容">CSS 内容</h3>
+
+<pre class="brush: css">body {
+ font-size: 0.8em;
+ font-family: Sans-Serif;
+}
+
+form {
+ background-color: #cccccc;
+ border-radius: 0.3em;
+ display: inline-block;
+ margin-bottom: 0.5em;
+ padding: 1em;
+}
+
+table {
+ border-collapse: collapse;
+}
+
+input {
+ padding: 0.3em;
+ border-color: #cccccc;
+ border-radius: 0.3em;
+}
+
+.required:after {
+ content: "*";
+ color: red;
+}
+
+.button-pane {
+ margin-top: 1em;
+}
+
+#pub-viewer {
+ float: right;
+ width: 48%;
+ height: 20em;
+ border: solid #d092ff 0.1em;
+}
+#pub-viewer iframe {
+ width: 100%;
+ height: 100%;
+}
+
+#pub-list {
+ width: 46%;
+ background-color: #eeeeee;
+ border-radius: 0.3em;
+}
+#pub-list li {
+ padding-top: 0.5em;
+ padding-bottom: 0.5em;
+ padding-right: 0.5em;
+}
+
+#msg {
+ margin-bottom: 1em;
+}
+
+.action-success {
+ padding: 0.5em;
+ color: #00d21e;
+ background-color: #eeeeee;
+ border-radius: 0.2em;
+}
+
+.action-failure {
+ padding: 0.5em;
+ color: #ff1408;
+ background-color: #eeeeee;
+ border-radius: 0.2em;
+}
+
+.note {
+ font-size: smaller;
+}
+
+.destructive {
+ background-color: orange;
+}
+.destructive:hover {
+ background-color: #ff8000;
+}
+.destructive:active {
+ background-color: red;
+}</pre>
+
+<h3 id="JavaScript_内容">JavaScript 内容</h3>
+
+<pre class="brush: js">(function () {
+ var COMPAT_ENVS = [
+ ['Firefox', "&gt;= 16.0"],
+ ['Google Chrome',
+ "&gt;= 24.0 (you may need to get Google Chrome Canary), NO Blob storage support"]
+ ];
+ var compat = $('#compat');
+ compat.empty();
+ compat.append('&lt;ul id="compat-list"&gt;&lt;/ul&gt;');
+ COMPAT_ENVS.forEach(function(val, idx, array) {
+ $('#compat-list').append('&lt;li&gt;' + val[0] + ': ' + val[1] + '&lt;/li&gt;');
+ });
+
+ const DB_NAME = 'mdn-demo-indexeddb-epublications';
+ const DB_VERSION = 1; // Use a long long for this value (don't use a float)
+ const DB_STORE_NAME = 'publications';
+
+ var db;
+
+ // Used to keep track of which view is displayed to avoid uselessly reloading it
+ var current_view_pub_key;
+
+ function openDb() {
+ console.log("openDb ...");
+ var req = indexedDB.open(DB_NAME, DB_VERSION);
+ req.onsuccess = function (evt) {
+ // Better use "this" than "req" to get the result to avoid problems with
+ // garbage collection.
+ // db = req.result;
+ db = this.result;
+ console.log("openDb DONE");
+ };
+ req.onerror = function (evt) {
+ console.error("openDb:", evt.target.errorCode);
+ };
+
+ req.onupgradeneeded = function (evt) {
+ console.log("openDb.onupgradeneeded");
+ var store = evt.currentTarget.result.createObjectStore(
+ DB_STORE_NAME, { keyPath: 'id', autoIncrement: true });
+
+ store.createIndex('biblioid', 'biblioid', { unique: true });
+ store.createIndex('title', 'title', { unique: false });
+ store.createIndex('year', 'year', { unique: false });
+ };
+ }
+
+ /**
+ * @param {string} store_name
+ * @param {string} mode either "readonly" or "readwrite"
+ */
+ function getObjectStore(store_name, mode) {
+ var tx = db.transaction(store_name, mode);
+ return tx.objectStore(store_name);
+ }
+
+ function clearObjectStore(store_name) {
+ var store = getObjectStore(DB_STORE_NAME, 'readwrite');
+ var req = store.clear();
+ req.onsuccess = function(evt) {
+ displayActionSuccess("Store cleared");
+ displayPubList(store);
+ };
+ req.onerror = function (evt) {
+ console.error("clearObjectStore:", evt.target.errorCode);
+ displayActionFailure(this.error);
+ };
+ }
+
+ function getBlob(key, store, success_callback) {
+ var req = store.get(key);
+ req.onsuccess = function(evt) {
+ var value = evt.target.result;
+ if (value)
+ success_callback(value.blob);
+ };
+ }
+
+ /**
+ * @param {IDBObjectStore=} store
+ */
+ function displayPubList(store) {
+ console.log("displayPubList");
+
+ if (typeof store == 'undefined')
+ store = getObjectStore(DB_STORE_NAME, 'readonly');
+
+ var pub_msg = $('#pub-msg');
+ pub_msg.empty();
+ var pub_list = $('#pub-list');
+ pub_list.empty();
+ // Resetting the iframe so that it doesn't display previous content
+ newViewerFrame();
+
+ var req;
+ req = store.count();
+ // Requests are executed in the order in which they were made against the
+ // transaction, and their results are returned in the same order.
+ // Thus the count text below will be displayed before the actual pub list
+ // (not that it is algorithmically important in this case).
+ req.onsuccess = function(evt) {
+ pub_msg.append('&lt;p&gt;There are &lt;strong&gt;' + evt.target.result +
+ '&lt;/strong&gt; record(s) in the object store.&lt;/p&gt;');
+ };
+ req.onerror = function(evt) {
+ console.error("add error", this.error);
+ displayActionFailure(this.error);
+ };
+
+ var i = 0;
+ req = store.openCursor();
+ req.onsuccess = function(evt) {
+ var cursor = evt.target.result;
+
+ // If the cursor is pointing at something, ask for the data
+ if (cursor) {
+ console.log("displayPubList cursor:", cursor);
+ req = store.get(cursor.key);
+ req.onsuccess = function (evt) {
+ var value = evt.target.result;
+ var list_item = $('&lt;li&gt;' +
+ '[' + cursor.key + '] ' +
+ '(biblioid: ' + value.biblioid + ') ' +
+ value.title +
+ '&lt;/li&gt;');
+ if (value.year != null)
+ list_item.append(' - ' + value.year);
+
+ if (value.hasOwnProperty('blob') &amp;&amp;
+ typeof value.blob != 'undefined') {
+ var link = $('&lt;a href="' + cursor.key + '"&gt;File&lt;/a&gt;');
+ link.on('click', function() { return false; });
+ link.on('mouseenter', function(evt) {
+ setInViewer(evt.target.getAttribute('href')); });
+ list_item.append(' / ');
+ list_item.append(link);
+ } else {
+ list_item.append(" / No attached file");
+ }
+ pub_list.append(list_item);
+ };
+
+ // Move on to the next object in store
+ cursor.continue();
+
+ // This counter serves only to create distinct ids
+ i++;
+ } else {
+ console.log("No more entries");
+ }
+ };
+ }
+
+ function newViewerFrame() {
+ var viewer = $('#pub-viewer');
+ viewer.empty();
+ var iframe = $('&lt;iframe /&gt;');
+ viewer.append(iframe);
+ return iframe;
+ }
+
+ function setInViewer(key) {
+ console.log("setInViewer:", arguments);
+ key = Number(key);
+ if (key == current_view_pub_key)
+ return;
+
+ current_view_pub_key = key;
+
+ var store = getObjectStore(DB_STORE_NAME, 'readonly');
+ getBlob(key, store, function(blob) {
+ console.log("setInViewer blob:", blob);
+ var iframe = newViewerFrame();
+
+ // It is not possible to set a direct link to the
+ // blob to provide a mean to directly download it.
+ if (blob.type == 'text/html') {
+ var reader = new FileReader();
+ reader.onload = (function(evt) {
+ var html = evt.target.result;
+ iframe.load(function() {
+ $(this).contents().find('html').html(html);
+ });
+ });
+ reader.readAsText(blob);
+ } else if (blob.type.indexOf('image/') == 0) {
+ iframe.load(function() {
+ var img_id = 'image-' + key;
+ var img = $('&lt;img id="' + img_id + '"/&gt;');
+ $(this).contents().find('body').html(img);
+ var obj_url = window.URL.createObjectURL(blob);
+ $(this).contents().find('#' + img_id).attr('src', obj_url);
+ window.URL.revokeObjectURL(obj_url);
+ });
+ } else if (blob.type == 'application/pdf') {
+ $('*').css('cursor', 'wait');
+ var obj_url = window.URL.createObjectURL(blob);
+ iframe.load(function() {
+ $('*').css('cursor', 'auto');
+ });
+ iframe.attr('src', obj_url);
+ window.URL.revokeObjectURL(obj_url);
+ } else {
+ iframe.load(function() {
+ $(this).contents().find('body').html("No view available");
+ });
+ }
+
+ });
+ }
+
+ /**
+ * @param {string} biblioid
+ * @param {string} title
+ * @param {number} year
+ * @param {string} url the URL of the image to download and store in the local
+ * IndexedDB database. The resource behind this URL is subjected to the
+ * "Same origin policy", thus for this method to work, the URL must come from
+ * the same origin as the web site/app this code is deployed on.
+ */
+ function addPublicationFromUrl(biblioid, title, year, url) {
+ console.log("addPublicationFromUrl:", arguments);
+
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', url, true);
+ // Setting the wanted responseType to "blob"
+ // http://www.w3.org/TR/XMLHttpRequest2/#the-response-attribute
+ xhr.responseType = 'blob';
+ xhr.onload = function (evt) {
+ if (xhr.status == 200) {
+ console.log("Blob retrieved");
+ var blob = xhr.response;
+ console.log("Blob:", blob);
+ addPublication(biblioid, title, year, blob);
+ } else {
+ console.error("addPublicationFromUrl error:",
+ xhr.responseText, xhr.status);
+ }
+ };
+ xhr.send();
+
+ // We can't use jQuery here because as of jQuery 1.8.3 the new "blob"
+ // responseType is not handled.
+ // http://bugs.jquery.com/ticket/11461
+ // http://bugs.jquery.com/ticket/7248
+ // $.ajax({
+ // url: url,
+ // type: 'GET',
+ // xhrFields: { responseType: 'blob' },
+ // success: function(data, textStatus, jqXHR) {
+ // console.log("Blob retrieved");
+ // console.log("Blob:", data);
+ // // addPublication(biblioid, title, year, data);
+ // },
+ // error: function(jqXHR, textStatus, errorThrown) {
+ // console.error(errorThrown);
+ // displayActionFailure("Error during blob retrieval");
+ // }
+ // });
+ }
+
+ /**
+ * @param {string} biblioid
+ * @param {string} title
+ * @param {number} year
+ * @param {Blob=} blob
+ */
+ function addPublication(biblioid, title, year, blob) {
+ console.log("addPublication arguments:", arguments);
+ var obj = { biblioid: biblioid, title: title, year: year };
+ if (typeof blob != 'undefined')
+ obj.blob = blob;
+
+ var store = getObjectStore(DB_STORE_NAME, 'readwrite');
+ var req;
+ try {
+ req = store.add(obj);
+ } catch (e) {
+ if (e.name == 'DataCloneError')
+ displayActionFailure("This engine doesn't know how to clone a Blob, " +
+ "use Firefox");
+ throw e;
+ }
+ req.onsuccess = function (evt) {
+ console.log("Insertion in DB successful");
+ displayActionSuccess();
+ displayPubList(store);
+ };
+ req.onerror = function() {
+ console.error("addPublication error", this.error);
+ displayActionFailure(this.error);
+ };
+ }
+
+ /**
+ * @param {string} biblioid
+ */
+ function deletePublicationFromBib(biblioid) {
+ console.log("deletePublication:", arguments);
+ var store = getObjectStore(DB_STORE_NAME, 'readwrite');
+ var req = store.index('biblioid');
+ req.get(biblioid).onsuccess = function(evt) {
+ if (typeof evt.target.result == 'undefined') {
+ displayActionFailure("No matching record found");
+ return;
+ }
+ deletePublication(evt.target.result.id, store);
+ };
+ req.onerror = function (evt) {
+ console.error("deletePublicationFromBib:", evt.target.errorCode);
+ };
+ }
+
+ /**
+ * @param {number} key
+ * @param {IDBObjectStore=} store
+ */
+ function deletePublication(key, store) {
+ console.log("deletePublication:", arguments);
+
+ if (typeof store == 'undefined')
+ store = getObjectStore(DB_STORE_NAME, 'readwrite');
+
+ // As per spec http://www.w3.org/TR/IndexedDB/#object-store-deletion-operation
+ // the result of the Object Store Deletion Operation algorithm is
+ // undefined, so it's not possible to know if some records were actually
+ // deleted by looking at the request result.
+ var req = store.get(key);
+ req.onsuccess = function(evt) {
+ var record = evt.target.result;
+ console.log("record:", record);
+ if (typeof record == 'undefined') {
+ displayActionFailure("No matching record found");
+ return;
+ }
+ // Warning: The exact same key used for creation needs to be passed for
+ // the deletion. If the key was a Number for creation, then it needs to
+ // be a Number for deletion.
+ req = store.delete(key);
+ req.onsuccess = function(evt) {
+ console.log("evt:", evt);
+ console.log("evt.target:", evt.target);
+ console.log("evt.target.result:", evt.target.result);
+ console.log("delete successful");
+ displayActionSuccess("Deletion successful");
+ displayPubList(store);
+ };
+ req.onerror = function (evt) {
+ console.error("deletePublication:", evt.target.errorCode);
+ };
+ };
+ req.onerror = function (evt) {
+ console.error("deletePublication:", evt.target.errorCode);
+ };
+ }
+
+ function displayActionSuccess(msg) {
+ msg = typeof msg != 'undefined' ? "Success: " + msg : "Success";
+ $('#msg').html('&lt;span class="action-success"&gt;' + msg + '&lt;/span&gt;');
+ }
+ function displayActionFailure(msg) {
+ msg = typeof msg != 'undefined' ? "Failure: " + msg : "Failure";
+ $('#msg').html('&lt;span class="action-failure"&gt;' + msg + '&lt;/span&gt;');
+ }
+ function resetActionStatus() {
+ console.log("resetActionStatus ...");
+ $('#msg').empty();
+ console.log("resetActionStatus DONE");
+ }
+
+ function addEventListeners() {
+ console.log("addEventListeners");
+
+ $('#register-form-reset').click(function(evt) {
+ resetActionStatus();
+ });
+
+ $('#add-button').click(function(evt) {
+ console.log("add ...");
+ var title = $('#pub-title').val();
+ var biblioid = $('#pub-biblioid').val();
+ if (!title || !biblioid) {
+ displayActionFailure("Required field(s) missing");
+ return;
+ }
+ var year = $('#pub-year').val();
+ if (year != '') {
+ // Better use Number.isInteger if the engine has EcmaScript 6
+ if (isNaN(year)) {
+ displayActionFailure("Invalid year");
+ return;
+ }
+ year = Number(year);
+ } else {
+ year = null;
+ }
+
+ var file_input = $('#pub-file');
+ var selected_file = file_input.get(0).files[0];
+ console.log("selected_file:", selected_file);
+ // Keeping a reference on how to reset the file input in the UI once we
+ // have its value, but instead of doing that we rather use a "reset" type
+ // input in the HTML form.
+ //file_input.val(null);
+ var file_url = $('#pub-file-url').val();
+ if (selected_file) {
+ addPublication(biblioid, title, year, selected_file);
+ } else if (file_url) {
+ addPublicationFromUrl(biblioid, title, year, file_url);
+ } else {
+ addPublication(biblioid, title, year);
+ }
+
+ });
+
+ $('#delete-button').click(function(evt) {
+ console.log("delete ...");
+ var biblioid = $('#pub-biblioid-to-delete').val();
+ var key = $('#key-to-delete').val();
+
+ if (biblioid != '') {
+ deletePublicationFromBib(biblioid);
+ } else if (key != '') {
+ // Better use Number.isInteger if the engine has EcmaScript 6
+ if (key == '' || isNaN(key)) {
+ displayActionFailure("Invalid key");
+ return;
+ }
+ key = Number(key);
+ deletePublication(key);
+ }
+ });
+
+ $('#clear-store-button').click(function(evt) {
+ clearObjectStore();
+ });
+
+ var search_button = $('#search-list-button');
+ search_button.click(function(evt) {
+ displayPubList();
+ });
+
+ }
+
+ openDb();
+ addEventListeners();
+
+})(); // Immediately-Invoked Function Expression (IIFE)</pre>
+
+<p>{{ LiveSampleLink('Full_IndexedDB_example', "Test the online live demo") }}</p>
+
+<div class="blockIndicator note">
+<p><strong>注意:</strong><code>window.indexedDB.open()</code> 是异步的。该方法在 <code>success</code> 事件触发前很长一段时间就执行完毕。这意味着一个调用 <code>open()</code> 和 <code>onsuccess</code> 的方法(例如 <code>openDb()</code>),会在 <code>onsuccess</code> 句柄开始运行前就已经返回了。这种情况同样适用于其他请求方法,比如 <code>transaction()</code> 和 <code>get()</code>。</p>
+</div>
+
+<h2 id="另请参阅">另请参阅</h2>
+
+<p>如有需要,请进一步阅读以获取更多信息。</p>
+
+<h3 id="参考">参考</h3>
+
+<ul>
+ <li><a href="https://developer.mozilla.org/en/IndexedDB" title="https://developer.mozilla.org/en/IndexedDB">IndexedDB 接口参考</a></li>
+ <li><a href="http://www.w3.org/TR/IndexedDB/" title="http://www.w3.org/TR/IndexedDB/">Indexed Database 接口说明</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/IndexedDB/Using_IndexedDB_in_chrome">在 Chrome 中使用 IndexedDB</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Using_JavaScript_Generators_in_Firefox">在 Firefox 中使用 JavaScript 生成器</a></li>
+ <li>Firefox 源码中的 IndexedDB <a href="https://mxr.mozilla.org/mozilla-central/find?text=&amp;string=dom%2FindexedDB%2F.*%5C.idl&amp;regexp=1">接口文件</a></li>
+</ul>
+
+<h3 id="教程和指导">教程和指导</h3>
+
+<ul>
+ <li><a href="http://www.html5rocks.com/en/tutorials/indexeddb/uidatabinding/" title="http://www.html5rocks.com/en/tutorials/indexeddb/uidatabinding/">Databinding UI Elements with IndexedDB</a></li>
+ <li><a href="http://msdn.microsoft.com/en-us/scriptjunkie/gg679063.aspx" title="http://msdn.microsoft.com/en-us/scriptjunkie/gg679063.aspx">IndexedDB — The Store in Your Browser</a></li>
+</ul>
+
+<h3 id="库">库</h3>
+
+<ul>
+ <li><a href="https://localforage.github.io/localForage/">localForage</a>: 一个提供 name:value 的简单语法的客户端数据存储垫片(Polyfill),它基于 IndexedDB 实现,并在不持支 IndexedDB 的浏览器中自动回退只 WebSQL 和 localStorage。</li>
+ <li><a href="http://www.dexie.org/">dexie.js</a>: 对 IndexedDB 的封装,通过提供更友好和简单语法以进行快速的编码开发。</li>
+ <li><a href="https://github.com/erikolson186/zangodb">ZangoDB</a>: 一个类 MongoDB 的 IndexedDB 接口实现,提供了诸如过滤、投影、排序、更新和聚合等大多数 MongoDB 常见的特性。</li>
+ <li><a href="http://jsstore.net/">JsStore</a>: 一个具备类 SQL 语法的简单和先进的 IndexedDB 封装实现。</li>
+</ul>
diff --git a/files/zh-cn/web/api/indexeddb_api/using_indexeddb_in_chrome/index.html b/files/zh-cn/web/api/indexeddb_api/using_indexeddb_in_chrome/index.html
new file mode 100644
index 0000000000..d9d9aa2cbf
--- /dev/null
+++ b/files/zh-cn/web/api/indexeddb_api/using_indexeddb_in_chrome/index.html
@@ -0,0 +1,27 @@
+---
+title: Using IndexedDB in chrome
+slug: Web/API/IndexedDB_API/Using_IndexedDB_in_chrome
+translation_of: Mozilla/Tech/XPCOM/Using_IndexedDB_in_chrome
+---
+<p>indexedDB API 通常被用来在用户的浏览器端存储来自JavaScript的数据。(点击 <a href="/en-US/docs/IndexedDB/Using_IndexedDB" title="/en-US/docs/IndexedDB/Using_IndexedDB">Using IndexedDB</a> 复习)。然而,使用system-privileged JavaScript的 <code><a href="/en-US/docs/Mozilla/Tech/XPCOM/Language_Bindings/Components.utils.importGlobalProperties">Components.utils.importGlobalProperties()</a> </code>函数也能获取这些API:</p>
+
+<pre class="brush: js">Components.utils.importGlobalProperties(["indexedDB"]);
+
+// From here on, it's like using IndexedDB from content
+var req = indexedDB.open("my-database");
+// ...</pre>
+
+<p>如果你要创建一个 sandbox ,并且打算<code>在sandbox中使用indexedDB,那么在</code> <code><a href="/en-US/docs/Mozilla/Tech/XPCOM/Language_Bindings/Components.utils.Sandbox">Sandbox</a></code> 的构造函数中使用 <code>wantGlobalProperties配置项:</code></p>
+
+<pre class="brush: js">var options = {
+ "wantGlobalProperties": ["indexedDB"]
+}
+var principal = Cc["@mozilla.org/systemprincipal;1"].createInstance(Ci.nsIPrincipal);
+var sandbox = Components.utils.Sandbox(principal, options);
+
+// The sandbox will have access to indexedDB
+var sandboxScript = 'var req = indexedDB.open("my-database");';
+Components.utils.evalInSandbox(sandboxScript, sandbox);
+</pre>
+
+<p>在Firefox 33之前,你可能要使用<code>nsIIndexedDatabaseManager的initWindowless</code>方法,从chrome的代码中方法获取indexedDB。这种方法在Firefox 33时,已经被移除。</p>