From ef48a5523a1722ddeb3f6eaf21508d1d40803a76 Mon Sep 17 00:00:00 2001 From: Maricaya <915270549@qq.com> Date: Sun, 29 Mar 2020 23:11:05 +0800 Subject: [PATCH 1/7] update translation of indexdb --- 6-data-storage/03-indexeddb/article.md | 89 ++++++++++--------- .../03-indexeddb/indexeddb-structure.svg | 8 +- 2 files changed, 49 insertions(+), 48 deletions(-) diff --git a/6-data-storage/03-indexeddb/article.md b/6-data-storage/03-indexeddb/article.md index a53713cbc4..73e86e74b4 100644 --- a/6-data-storage/03-indexeddb/article.md +++ b/6-data-storage/03-indexeddb/article.md @@ -5,55 +5,55 @@ libs: # IndexedDB -IndexedDB is a built-in database, much more powerful than `localStorage`. +indexedDB是一个内置的数据库,它比 `localStorage` 强大的多。 -- Key/value storage: value can be (almost) anything, multiple key types. -- Supports transactions for reliability. -- Supports key range queries, indexes. -- Can store much more data than `localStorage`. +- 键/值 储存:值(几乎)可以是任何类型,键有多种类型。 +- 支撑事务的可靠性。 +- 支持键范围查询、索引。 +- 和 `localStorage` 相比,它可以存储更多数据 。 -That power is usually excessive for traditional client-server apps. IndexedDB is intended for offline apps, to be combined with ServiceWorkers and other technologies. +对于传统的 客户端-服务器 应用,这些功能通常是没有必要的。IndexedDB 适用于离线应用,可与 ServiceWorkers 和其他技术相结合使用。 -The native interface to IndexedDB, described in the specification , is event-based. +根据规范 中的描述,IndexedDB 的本机接口是基于事件的。 -We can also use `async/await` with the help of a promise-based wrapper, like . That's pretty convenient, but the wrapper is not perfect, it can't replace events for all cases. So we'll start with events, and then, after we gain understanding of IndexedDb, we'll use the wrapper. +我们还可以在基于 promise 的包装器(wrapper),如 的帮助下使用 `async/await` 。这要方便的多,但是包装器并不完美,它并不能替代所有情况下的事件。因此,我们先练习事件(events),在理解 IndexedDB 之后,我们将使用包装器。 -## Open database +## 打开数据库 -To start working with IndexedDB, we first need to open a database. +要想使用 IndexedDB,首先需要打开一个数据库。 -The syntax: +语法: ```js let openRequest = indexedDB.open(name, version); ``` -- `name` -- a string, the database name. -- `version` -- a positive integer version, by default `1` (explained below). +- `name` —— 字符串,即数据库名称。 +- `version` —— 一个正整数版本,默认为 `1`(下面解释)。 -We can have many databases with different names, but all of them exist within the current origin (domain/protocol/port). Different websites can't access databases of each other. +数据库可以有许多不同的名称,但是必须存在于当前的源(域/协议/端口)中。不同的网站不能相互访问对方的数据库。 -After the call, we need to listen to events on `openRequest` object: -- `success`: database is ready, there's the "database object" in `openRequest.result`, that we should use it for further calls. -- `error`: open failed. -- `upgradeneeded`: database version is outdated (see below). +调用之后,需要监听 `openRequest` 对象上的事件: +- `success`: 数据库准备就绪,`openRequest.result` 中有了一个数据库对象"Database Object",使用它进行进一步的调用。 +- `error`: 打开失败。 +- `upgradeneeded`: 数据库版本已过时(参见下文)。 -**IndexedDB has a built-in mechanism of "schema versioning", absent in server-side databases.** +**IndexedDB 具有内建的“模式版本控制”机制,这在服务器端数据库中是不存在的。** -Unlike server-side databases, IndexedDB is client-side, in the browser, so we don't have the data at hands. But when we publish a new version of our app, we may need to update the database. +与服务器端数据库不同,IndexedDB 存在于客户端,数据存储在浏览器中。因此开发人员不能直接访问它。但当新版本的应用程序发布之后,我们可能需要更新数据库。 -If the local database version is less than specified in `open`, then a special event `upgradeneeded` is triggered, and we can compare versions and upgrade data structures as needed. +如果本地数据库版本低于 `open` 中指定的版本,会触发一个特殊事件 `upgradeneeded`。我们可以根据需要比较版本和升级数据结构。 -The event also triggers when the database did not exist yet, so we can perform initialization. +当数据库还不存在的时候,也会触发这个事件。因此,我们应该先执行初始化。 -For instance, when we first publish our app, we open it with version `1` and perform the initialization in `upgradeneeded` handler: +例如,当我们第一次发布应用程序时,使用版本 `1` 打开它,并在 `upgradeneeded` 处理程序中执行初始化: ```js let openRequest = indexedDB.open("store", *!*1*/!*); openRequest.onupgradeneeded = function() { - // triggers if the client had no database - // ...perform initialization... + // 如果客户端没有数据库则触发 + // ...执行初始化... }; openRequest.onerror = function() { @@ -62,54 +62,55 @@ openRequest.onerror = function() { openRequest.onsuccess = function() { let db = openRequest.result; - // continue to work with database using db object + // 继续使用 db 对象处理数据库 }; ``` -When we publish the 2nd version: +当我们发布第二个版本时: ```js let openRequest = indexedDB.open("store", *!*2*/!*); -// check the existing database version, do the updates if needed: +// 检查现有数据库版本,并根据需要进行更新: openRequest.onupgradeneeded = function() { let db = openRequest.result; - switch(db.version) { // existing (old) db version + switch(db.version) { // 现有(旧)数据库版本 case 0: - // version 0 means that the client had no database - // perform initialization + // 版本 0 表示客户端没有数据库 + // 执行初始化 case 1: - // client had version 1 - // update + // 客户端版本为 1 + // 更新 } }; ``` -After `openRequest.onsuccess` we have the database object in `openRequest.result`, that we'll use for further operations. +在 `openRequest.onsuccess` 之后,我们在 `openRequest.result` 中有一个数据库对象,将用于进一步的操作。 -To delete a database: +删除数据库: ```js let deleteRequest = indexedDB.deleteDatabase(name) -// deleteRequest.onsuccess/onerror tracks the result +// deleteRequest.onsuccess/onerror 追踪(tracks)结果 ``` +## 对象库(object store) -## Object store +要在 `IndexedDB` 中存储某些内容,我们需要一个**对象库**。 -An object store is a core concept of IndexedDB. Counterparts in other databases are called "tables" or "collections". It's where the data is stored. A database may have multiple stores: one for users, another one for goods, etc. +对象库是 IndexedDB 的核心概念,在其他数据库中对应的对象称为“表”或“集合”。它是储存数据的地方。一个数据库可能有多个存储区:一个用于存储用户数据,另一个用于商品,等等。 -Despite being named an "object store", primitives can be stored too. +尽管被命名为“对象库”,但也可以存储原始类型。 -**We can store almost any value, including complex objects.** +**几乎可以存储任何值,包括复杂的对象。** -IndexedDB uses the [standard serialization algorithm](https://www.w3.org/TR/html53/infrastructure.html#section-structuredserializeforstorage) to clone-and-store an object. It's like `JSON.stringify`, but more powerful, capable of storing much more datatypes. +IndexedDB 使用[标准序列化算法](https://www.w3.org/TR/html53/infrastructure.html#section-structuredserializeforstorage)来克隆和存储对象。类似于 `JSON.stringify`,不过功能更加强大,能够存储更多的数据类型。 -An example of object that can't be stored: an object with circular references. Such objects are not serializable. `JSON.stringify` also fails for such objects. +有一种对象不能被存储:循环引用的对象。此类对象不可序列化,也不能进行 `JSON.stringify`。 -**There must be a unique `key` for every value in the store.** +**库中的每个值都必须有唯一的键 `key`** -A key must have a type one of: number, date, string, binary, or array. It's an unique identifier: we can search/remove/update values by the key. +键的类型必须为数字、日期、字符串、二进制或数组。它是唯一的标识符:通过键来 搜索/删除/更新 值。 ![](indexeddb-structure.svg) diff --git a/6-data-storage/03-indexeddb/indexeddb-structure.svg b/6-data-storage/03-indexeddb/indexeddb-structure.svg index 9fc1eb6ecd..54aa161c85 100644 --- a/6-data-storage/03-indexeddb/indexeddb-structure.svg +++ b/6-data-storage/03-indexeddb/indexeddb-structure.svg @@ -14,10 +14,10 @@ - Database + 数据库 - objectStore + 对象库 @@ -25,7 +25,7 @@ - objectStore + 对象库 @@ -45,7 +45,7 @@ - objectStore + 对象库 key1: value1 From b1d1d3fb09df2d28c09315b73a40cef25b03e517 Mon Sep 17 00:00:00 2001 From: Maricaya <915270549@qq.com> Date: Mon, 30 Mar 2020 13:20:36 +0800 Subject: [PATCH 2/7] fix spaces in indexeddb /done --- 6-data-storage/03-indexeddb/article.md | 89 +++++++++++++------------- 1 file changed, 45 insertions(+), 44 deletions(-) diff --git a/6-data-storage/03-indexeddb/article.md b/6-data-storage/03-indexeddb/article.md index e4bff6f3cc..1de2978013 100644 --- a/6-data-storage/03-indexeddb/article.md +++ b/6-data-storage/03-indexeddb/article.md @@ -5,18 +5,18 @@ libs: # IndexedDB -indexedDB是一个内置的数据库,它比 `localStorage` 强大的多。 +indexedDB 是一个内置的数据库,它比 `localStorage` 强大得多。 - 键/值 储存:值(几乎)可以是任何类型,键有多种类型。 - 支撑事务的可靠性。 - 支持键范围查询、索引。 -- 和 `localStorage` 相比,它可以存储更多数据 。 +- 和 `localStorage` 相比,它可以存储更多数据。 对于传统的 客户端-服务器 应用,这些功能通常是没有必要的。IndexedDB 适用于离线应用,可与 ServiceWorkers 和其他技术相结合使用。 根据规范 中的描述,IndexedDB 的本机接口是基于事件的。 -我们还可以在基于 promise 的包装器(wrapper),如 的帮助下使用 `async/await` 。这要方便的多,但是包装器并不完美,它并不能替代所有情况下的事件。因此,我们先练习事件(events),在理解 IndexedDB 之后,我们将使用包装器。 +我们还可以在基于 promise 的包装器(wrapper),如 的帮助下使用 `async/await`。这要方便的多,但是包装器并不完美,它并不能替代所有情况下的事件。因此,我们先练习事件(events),在理解 IndexedDB 之后,我们将使用包装器。 ## 打开数据库 @@ -34,9 +34,9 @@ let openRequest = indexedDB.open(name, version); 数据库可以有许多不同的名称,但是必须存在于当前的源(域/协议/端口)中。不同的网站不能相互访问对方的数据库。 调用之后,需要监听 `openRequest` 对象上的事件: -- `success`: 数据库准备就绪,`openRequest.result` 中有了一个数据库对象"Database Object",使用它进行进一步的调用。 -- `error`: 打开失败。 -- `upgradeneeded`: 数据库已准备就绪,但其版本已过时(见下文)。 +- `success`:数据库准备就绪,`openRequest.result` 中有了一个数据库对象“Database Object”,使用它进行进一步的调用。 +- `error`:打开失败。 +- `upgradeneeded`:数据库已准备就绪,但其版本已过时(见下文)。 **IndexedDB 具有内建的“模式版本控制”机制,这在服务器端数据库中是不存在的。** @@ -74,10 +74,10 @@ openRequest.onsuccess = function() { let openRequest = indexedDB.open("store", *!*2*/!*); openRequest.onupgradeneeded = function() { - // 现有的数据库版本小于2(或不存在) + // 现有的数据库版本小于 2(或不存在) let db = openRequest.result; - switch(db.version) { // 现有的db版本 + switch(db.version) { // 现有的 db 版本 case 0: // 版本 0 表示客户端没有数据库 // 执行初始化 @@ -100,7 +100,7 @@ let deleteRequest = indexedDB.deleteDatabase(name) ``` ```warn header="我们可以打开旧版本吗?" -如果我们想打开一个比当前版本更低的数据库,该怎么办?例如,现有的数据库版本是3,但我们想打开版本2 `open(...2)`。 +如果我们想打开一个比当前版本更低的数据库,该怎么办?例如,现有的数据库版本是 3,但我们想打开版本 2 `open(...2)`。 报错,触发 `openRequest.onerror`。 @@ -115,7 +115,7 @@ let deleteRequest = indexedDB.deleteDatabase(name) 这时网站更新到版本 2,这个用户在另一网页下打开了网站。这时两个网页都是我们的网站,但一个与数据库版本 1 有开放连接,而另一个试图在 `upgradeneeded` 处理程序中更新。 -问题是,这两个网页是同一个站点,同一个来源,共享同一个数据库。而数据库不能同时为版本 1 和版本 2。要执行版本 2 的更新,必须关闭版本 1 的所有连接。并= +问题是,这两个网页是同一个站点,同一个来源,共享同一个数据库。而数据库不能同时为版本 1 和版本 2。要执行版本 2 的更新,必须关闭版本 1 的所有连接。 为了完成这些,当尝试并行更新时,`versionchange` 事件会触发一个打开的数据库对象。我们应该监听这个对象,关闭数据库(还应该建议访问者重新加载页面,获取最新的代码)。 @@ -173,7 +173,7 @@ IndexedDB 使用[标准序列化算法](https://www.w3.org/TR/html53/infrastruct 有一种对象不能被存储:循环引用的对象。此类对象不可序列化,也不能进行 `JSON.stringify`。 -**库中的每个值都必须有唯一的键 `key`** +**库中的每个值都必须有唯一的键 `key`** 键的类型必须为数字、日期、字符串、二进制或数组。它是唯一的标识符:通过键来 搜索/删除/更新 值。 @@ -211,7 +211,7 @@ db.createObjectStore('books', {keyPath: 'id'}); 要执行数据库版本升级,主要有两种方法: 1. 我们跨域实现每个版本的升级功能:从 1 到 2,从 2 到 3,从 3 到 4,等等。在 `upgradeneeded` 中,可以进行版本比较(例如,老版本是 2,需要升级到 4),并针对每个中间版本(2 到 3,然后 3 到 4)逐步运行每个版本的升级。 -2. 或者我们可以检查数据库:以 `db.objectStoreNames` 的形式获取现有对象库的列表。该对象是一个 [DOMStringList](https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#domstringlist) 提供 `contains(name)` 方法来检查 name 是否存在,再根据存在和不存在的内容进行更新。 +2. 或者我们可以检查数据库:以 `db.objectStoreNames` 的形式获取现有对象库的列表。该对象是一个 [DOMStringList](https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#domstringlist) 提供 `contains(name)` 方法来检查 name 是否存在,再根据存在和不存在的内容进行更新。 对于小型数据库,第二种方法可能更简单。 @@ -223,7 +223,7 @@ let openRequest = indexedDB.open("db", 2); // 创建/升级 数据库而无需版本检查 openRequest.onupgradeneeded = function() { let db = openRequest.result; - if (!db.objectStoreNames.contains('books')) { // 如果没有 "books" 数据 + if (!db.objectStoreNames.contains('books')) { // 如果没有 “books” 数据 db.createObjectStore('books', {keyPath: 'id'}); // 创造它 } }; @@ -258,7 +258,7 @@ db.deleteObjectStore('books') db.transaction(store[, type]); ``` -- `store` 是事务要访问的库名称,例如`"books"`。如果我们要访问多个库,则是库名称的数组。 +- `store` 是事务要访问的库名称,例如 `"books"`。如果我们要访问多个库,则是库名称的数组。 - `type` – 交易类型,以下类型之一: - `readonly` —— 只读,默认值。 - `readwrite` —— 只能读取和写入数据,而不能 创建/删除/更改 对象库。 @@ -302,7 +302,7 @@ request.onerror = function() { ``` 基本有四个步骤: 1. 创建一个事务,在(1)表明要访问的所有存储。 -2. 使用 `transaction.objectStore(name)` ,在(2)中获取存储对象。 +2. 使用 `transaction.objectStore(name)`,在(2)中获取存储对象。 3. 在(3)执行对对象库 `books.add(book)` 的请求。 4. …处理请求 成功/错误(4),还可以根据需要发出其他请求。 @@ -326,7 +326,7 @@ request.onerror = function() { 在下一个版本 3.0 规范中,可能会有一种手动方式来完成事务,但目前在 2.0 中还没有。 -**当所有事务的请求完成,并且[微任务队列](info:microtask-queue) 为空时,它将自动提交。** +**当所有事务的请求完成,并且[微任务队列](info:microtask-queue)为空时,它将自动提交。** 通常,我们可以假设事务在其所有请求完成时提交,并且当前代码完成。 @@ -334,7 +334,7 @@ request.onerror = function() { 事务自动提交原则有一个重要的副作用。不能在事务中间插入 `fetch`, `setTimeout` 等异步操作。IndexedDB 不会让事务等待这些操作完成。 -在下面的代码中,`request2`中的行 `(*)` 失败,因为事务已经提交,不能在其中发出任何请求: +在下面的代码中,`request2` 中的行 `(*)` 失败,因为事务已经提交,不能在其中发出任何请求: ```js let request1 = books.add(book); @@ -350,6 +350,7 @@ request1.onsuccess = function() { }); }; ``` + 这是因为 `fetch` 是一个异步操作,一个宏任务。事务在浏览器开始执行宏任务之前关闭。 IndexedDB 规范的作者认为事务应该是短期的。主要是性能原因。 @@ -461,26 +462,26 @@ request.onerror = function(event) { ## 通过键搜索 对象库有两种主要的搜索类型: -1. 通过一个键或一个键范围。即:通过在 “books” 中存储的 `book.id`。 +1. 通过一个键或一个键范围。即:通过在“books”中存储的 `book.id`。 2. 另一个对象字段,例如 `book.price`。 -首先,让我们来处理键和键区 `(1)` 。 +首先,让我们来处理键和键区 `(1)`。 涉及到的搜索方法,包括支持精确键,也包括所谓的“范围查询” —— [IDBKeyRange](https://www.w3.org/TR/IndexedDB/#keyrange) 对象指定一个“键范围”。 使用以下调用函数创建范围: -- `IDBKeyRange.lowerBound(lower, [open])` 表示: `≥lower` (如果 `open` 是 true,表示 `>lower`) -- `IDBKeyRange.upperBound(upper, [open])` 表示: `≤upper` (如果 `open` 是 true,表示 `lower`) +- `IDBKeyRange.upperBound(upper, [open])` 表示:`≤upper`(如果 `open` 是 true,表示 ` 。它使用 [promisified](info:promisify) IndexedDB方法创建全局 `idb` 对象。 +在本章,我们会进一步使用一个轻便的承诺包装器 。它使用 [promisified](info:promisify) IndexedDB 方法创建全局 `idb` 对象。 -然后,我们可以不使用 `onsuccess/onerror` ,而是这样写: +然后,我们可以不使用 `onsuccess/onerror`,而是这样写: ```js let db = await idb.openDb('store', 1, db => { @@ -735,7 +736,7 @@ try { 如果我们没有捕获到错误,那么程序将一直失败,直到外部最近的 `try..catch` 捕获到为止。 -未捕获的错误将成为 `window` 对象上的 “unhandled promise rejection” 事件。 +未捕获的错误将成为 `window` 对象上的“unhandled promise rejection”事件。 我们可以这样处理这种错误: @@ -775,7 +776,7 @@ await inventory.add({ id: 'js', price: 10, created: new Date() }); // 错误 ### 获取本机对象 -在内部,包装器执行本机IndexedDB请求,并添加 `onerror/onsuccess` 方法,并返回 rejects/resolves 结果的 promise。 +在内部,包装器执行本机 IndexedDB 请求,并添加 `onerror/onsuccess` 方法,并返回 rejects/resolves 结果的 promise。 在大多数情况下都可以运行, 示例在这 。 @@ -787,25 +788,25 @@ let promise = books.add(book); // 获取 promise 对象(不要 await 结果) let request = promise.request; // 本地请求对象 let transaction = request.transaction; // 本地事务对象 -// ...做些本地的IndexedDB的处理... +// ...做些本地的 IndexedDB 的处理... let result = await promise; // 如果仍然需要 ``` ## 总结 -IndexedDB 可以被认为是 “localStorage on steroids”。这是一个简单的键值对数据库,功能强大到足以支持离线应用,而且用起来比较简单。 +IndexedDB 可以被认为是“localStorage on steroids”。这是一个简单的键值对数据库,功能强大到足以支持离线应用,而且用起来比较简单。 -最好的指南是官方文档。[目前的版本](https://w3c.github.io/IndexedDB) 是2.0,但是[3.0]((https://w3c.github.io/IndexedDB/) 版本中的一些方法(差别不大)也得到部分支持。 +最好的指南是官方文档。[目前的版本](https://w3c.github.io/IndexedDB)是2.0,但是[3.0](https://w3c.github.io/IndexedDB/)版本中的一些方法(差别不大)也得到部分支持。 基本用法可以用几个短语来描述: -1. 获取一个promise包装器,比如 [idb](https://github.com/jakearchibald/idb). +1. 获取一个 promise 包装器,比如 [idb](https://github.com/jakearchibald/idb). 2. 打开一个数据库:`idb.openDb(name, version, onupgradeneeded)` - 在 `onupgradeneeded` 处理程序中创建对象存储和索引,或者根据需要执行版本更新。 3. 对于请求: - - 创建事务 `db.transaction('books')`(如果需要的话,设置readwrite)。 - - 获取对象存储 `transaction.objectStore('books')`. + - 创建事务 `db.transaction('books')`(如果需要的话,设置 readwrite)。 + - 获取对象存储 `transaction.objectStore('books')`。 4. 按键搜索,可以直接调用对象库上的方法。 - 要按对象字段搜索,需要创建索引。 5. 如果内存中容纳不下数据,请使用光标。 From f2c0ddc94ed921be71b400d4ba0ac5b6ea19ec12 Mon Sep 17 00:00:00 2001 From: Maricaya <915270549@qq.com> Date: Mon, 6 Apr 2020 21:25:11 +0800 Subject: [PATCH 3/7] reset svg of indexeddb --- 6-data-storage/03-indexeddb/indexeddb-structure.svg | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/6-data-storage/03-indexeddb/indexeddb-structure.svg b/6-data-storage/03-indexeddb/indexeddb-structure.svg index 54aa161c85..9fc1eb6ecd 100644 --- a/6-data-storage/03-indexeddb/indexeddb-structure.svg +++ b/6-data-storage/03-indexeddb/indexeddb-structure.svg @@ -14,10 +14,10 @@ - 数据库 + Database - 对象库 + objectStore @@ -25,7 +25,7 @@ - 对象库 + objectStore @@ -45,7 +45,7 @@ - 对象库 + objectStore key1: value1 From d15c71b40f77c3efdc4edbffcc7be0846b2db553 Mon Sep 17 00:00:00 2001 From: Maricaya <915270549@qq.com> Date: Mon, 13 Apr 2020 21:40:07 +0800 Subject: [PATCH 4/7] complete verification --- 6-data-storage/03-indexeddb/article.md | 60 +++++++++++++------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/6-data-storage/03-indexeddb/article.md b/6-data-storage/03-indexeddb/article.md index 1de2978013..ee088dbec2 100644 --- a/6-data-storage/03-indexeddb/article.md +++ b/6-data-storage/03-indexeddb/article.md @@ -5,7 +5,7 @@ libs: # IndexedDB -indexedDB 是一个内置的数据库,它比 `localStorage` 强大得多。 +IndexedDB 是一个内置的数据库,它比 `localStorage` 强大得多。 - 键/值 储存:值(几乎)可以是任何类型,键有多种类型。 - 支撑事务的可靠性。 @@ -39,11 +39,11 @@ let openRequest = indexedDB.open(name, version); - `upgradeneeded`:数据库已准备就绪,但其版本已过时(见下文)。 -**IndexedDB 具有内建的“模式版本控制”机制,这在服务器端数据库中是不存在的。** +**IndexedDB 具有内建的“模式(scheme)版本控制”机制,这在服务器端数据库中是不存在的。** 与服务器端数据库不同,IndexedDB 存在于客户端,数据存储在浏览器中。因此开发人员不能直接访问它。但当新版本的应用程序发布之后,我们可能需要更新数据库。 -如果本地数据库版本低于 `open` 中指定的版本,会触发一个特殊事件 `upgradeneeded`。我们可以根据需要比较版本和升级数据结构。 +如果本地数据库版本低于 `open` 中指定的版本,会触发一个特殊事件 `upgradeneeded`。我们可以根据需要比较版本并升级数据结构。 当数据库还不存在的时候,也会触发这个事件。因此,我们应该先执行初始化。 @@ -140,7 +140,7 @@ openRequest.onsuccess = function() { }; */!* - // ...数据库已经准备好,使用它... + // ……数据库已经准备好,请使用它…… }; *!* @@ -204,13 +204,13 @@ db.createObjectStore(name[, keyOptions]); db.createObjectStore('books', {keyPath: 'id'}); ``` -**在 `upgradeneeded` 程序中,只有在创建数据库版本时,对象库被才能被 创建/修改。** +**在 `upgradeneeded` 处理程序中,只有在创建数据库版本时,对象库被才能被 创建/修改。** 这是技术上的限制。在 upgradeneedHandler 之外,可以 添加/删除/更新数据,但是只能在版本更新期间 创建/删除/更改对象库。 要执行数据库版本升级,主要有两种方法: -1. 我们跨域实现每个版本的升级功能:从 1 到 2,从 2 到 3,从 3 到 4,等等。在 `upgradeneeded` 中,可以进行版本比较(例如,老版本是 2,需要升级到 4),并针对每个中间版本(2 到 3,然后 3 到 4)逐步运行每个版本的升级。 +1. 我们实现每个版本的升级功能:从 1 到 2,从 2 到 3,从 3 到 4,等等。在 `upgradeneeded` 中,可以进行版本比较(例如,老版本是 2,需要升级到 4),并针对每个中间版本(2 到 3,然后 3 到 4)逐步运行每个版本的升级。 2. 或者我们可以检查数据库:以 `db.objectStoreNames` 的形式获取现有对象库的列表。该对象是一个 [DOMStringList](https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#domstringlist) 提供 `contains(name)` 方法来检查 name 是否存在,再根据存在和不存在的内容进行更新。 对于小型数据库,第二种方法可能更简单。 @@ -259,7 +259,7 @@ db.transaction(store[, type]); ``` - `store` 是事务要访问的库名称,例如 `"books"`。如果我们要访问多个库,则是库名称的数组。 -- `type` – 交易类型,以下类型之一: +- `type` – 事务类型,以下类型之一: - `readonly` —— 只读,默认值。 - `readwrite` —— 只能读取和写入数据,而不能 创建/删除/更改 对象库。 @@ -304,7 +304,7 @@ request.onerror = function() { 1. 创建一个事务,在(1)表明要访问的所有存储。 2. 使用 `transaction.objectStore(name)`,在(2)中获取存储对象。 3. 在(3)执行对对象库 `books.add(book)` 的请求。 -4. …处理请求 成功/错误(4),还可以根据需要发出其他请求。 +4. ……处理请求 成功/错误(4),还可以根据需要发出其他请求。 对象库支持两种存储值的方法: @@ -326,7 +326,7 @@ request.onerror = function() { 在下一个版本 3.0 规范中,可能会有一种手动方式来完成事务,但目前在 2.0 中还没有。 -**当所有事务的请求完成,并且[微任务队列](info:microtask-queue)为空时,它将自动提交。** +**当所有事务的请求完成,并且 [微任务队列](info:microtask-queue) 为空时,它将自动提交。** 通常,我们可以假设事务在其所有请求完成时提交,并且当前代码完成。 @@ -345,7 +345,7 @@ request1.onsuccess = function() { let request2 = books.add(anotherBook); // (*) */!* request2.onerror = function() { - console.log(request2.error.name); // 事务激活错误 + console.log(request2.error.name); // TransactionInactiveError }; }); }; @@ -355,7 +355,7 @@ request1.onsuccess = function() { IndexedDB 规范的作者认为事务应该是短期的。主要是性能原因。 -值得注意的是,只读事务将存储“锁定”以进行写入。因此,如果应用程序的一部分启动了 `books` 对象库上的读写操作,那么希望执行相同操作的另一部分必须等待新事务“挂起”,直到第一个事务完成。如果事务处理需要很长时间,将会导致奇怪的延迟。 +值得注意的是,`readwrite` 事务将存储“锁定”以进行写入。因此,如果应用程序的一部分启动了 `books` 对象库上的 `readwrite` 操作,那么希望执行相同操作的另一部分必须等待新事务“挂起”,直到第一个事务完成。如果事务处理需要很长时间,将会导致奇怪的延迟。 那么,该怎么办? @@ -363,14 +363,14 @@ IndexedDB 规范的作者认为事务应该是短期的。主要是性能原因 如果需要在一个事务中把所有操作保持一致,更好的做法是将 IndexedDB 事务和“其他”异步内容分开。 -首先,执行 `fetch`,并根据需要准备数据。然后创建事务并执行所有数据库请求,就可以工作了。 +首先,执行 `fetch`,并根据需要准备数据。然后创建事务并执行所有数据库请求,然后就正常了。 为了检测到成功完成的时刻,我们可以监听 `transaction.oncomplete` 事件: ```js let transaction = db.transaction("books", "readwrite"); -// ...执行操作... +// ……执行操作…… transaction.oncomplete = function() { console.log("Transaction is complete"); // 事务执行完成 @@ -384,18 +384,18 @@ transaction.oncomplete = function() { ```js transaction.abort(); ``` -取消其中的请求所做的所有修改,并触发 `transaction.onabort` 事件。 +取消请求里所做的所有修改,并触发 `transaction.onabort` 事件。 ## 错误处理 写入请求可能会失败。 -这是意料之中的事,不仅是事物可能出错,还有其他原因。例如超过了存储配额。因此,必须做好请求失败的处理。 +这是意料之中的事,不仅是我们可能会犯的粗心失误,还有与事务本身相关的其他原因。例如超过了存储配额。因此,必须做好请求失败的处理。 **失败的请求将自动中止事务,并取消所有的更改。** -遇到需要不取消现有更改的情况下(例如尝试另一个请求)处理失败情况,并让事务继续的情况,可以调用 `request.onerror` 处理程序,在其中调用 `event.preventDefault()` 防止事务中止。 +在一些情况下,我们会想自己去处理失败事务(例如尝试另一个请求)并让它继续执行,而不是取消现有的更改。可以调用 `request.onerror` 处理程序,在其中调用 `event.preventDefault()` 防止事务中止。 在下面的示例中,添加了一本新书,键 (`id`) 与现有的书相同。`store.add` 方法生成一个 `"ConstraInterror"`。可以在不取消事务的情况下进行处理: @@ -407,7 +407,7 @@ let book = { id: 'js', price: 10 }; let request = transaction.objectStore("books").add(book); request.onerror = function(event) { - // 有相同id的对象存在时,发生ConstraintError + // 有相同 id 的对象存在时,发生 ConstraintError if (request.error.name == "ConstraintError") { console.log("Book with such id already exists"); // 处理错误 event.preventDefault(); // 不要中止事务 @@ -441,9 +441,9 @@ db.onerror = function(event) { }; ``` -…但是错误被完全处理了呢?报告这种情况不应该被报告。 +……但是错误被完全处理了呢?这种情况不应该被报告。 -我们可以通过在 `request.onerror` 中使用 `event.stopPropagation()` 来停止冒泡,从而停止 `db.onerror`。 +我们可以通过在 `request.onerror` 中使用 `event.stopPropagation()` 来停止冒泡,从而停止 `db.onerror` 事件。 ```js request.onerror = function(event) { @@ -465,7 +465,7 @@ request.onerror = function(event) { 1. 通过一个键或一个键范围。即:通过在“books”中存储的 `book.id`。 2. 另一个对象字段,例如 `book.price`。 -首先,让我们来处理键和键区 `(1)`。 +首先,让我们来处理键和键范围 `(1)`。 涉及到的搜索方法,包括支持精确键,也包括所谓的“范围查询” —— [IDBKeyRange](https://www.w3.org/TR/IndexedDB/#keyrange) 对象指定一个“键范围”。 @@ -528,7 +528,7 @@ objectStore.createIndex(name, keyPath, [options]); - **`keyPath`** —— 索引应该跟踪的对象字段的路径(我们将根据该字段进行搜索)。 - **`option`** —— 具有以下属性的可选对象: - **`unique`** —— 如果为true,则存储中只有一个对象在 `keyPath` 上具有给定值。如果我们尝试添加重复项,索引将生成错误。 - - **`multiEntry`** —— 只有 `keypath` 上的值是数组才时使用。这时,索引将默认把整个数组视为键。但是如果 `multiEntry` 为 true,那么索引将为该数组中的每个值保留一个存储对象的列表。所以数组成员成为了索引键。 + - **`multiEntry`** —— 只有 `keypath` 上的值是数组才时使用。这时,默认情况下,索引将默认把整个数组视为键。但是如果 `multiEntry` 为 true,那么索引将为该数组中的每个值保留一个存储对象的列表。所以数组成员成为了索引键。 在我们的示例中,是按照 `id` 键存储图书的。 @@ -580,7 +580,7 @@ request.onsuccess = function() { 我们还可以使用 `IDBKeyRange` 创建范围,并查找 便宜/贵 的书: ```js -// 查找价格<=5的书籍 +// 查找价格 <=5 的书籍 let request = priceIndex.getAll(IDBKeyRange.upperBound(5)); ``` @@ -615,7 +615,7 @@ request.onsuccess = function() { books.clear(); // 清除存储。 ``` -## 光标 +## 光标(Cursors) 像 `getAll/getAllKeys` 这样的方法,会返回一个 键/值 数组。 @@ -627,12 +627,12 @@ books.clear(); // 清除存储。 **光标是一种特殊的对象,它在给定查询的情况下遍历对象库,一次返回一个键/值,从而节省内存。** -由于对象库是按键在内部排序的,因此游标按键顺序(默认为升序)遍历存储。 +由于对象库是按键在内部排序的,因此光标按键顺序(默认为升序)遍历存储。 语法: ```js -// 类似于 getAll,但带有游标: +// 类似于 getAll,但带有光标: let request = store.openCursor(query, [direction]); // 获取键,而不是值(例如 getAllKeys):store.openKeyCursor @@ -642,7 +642,7 @@ let request = store.openCursor(query, [direction]); - **`direction`** 是一个可选参数,使用顺序是: - `"next"` —— 默认值,光标从有最小索引的记录向上移动。 - `"prev"` —— 相反的顺序:从有最大的索引的记录开始下降。 - - `"nextunique"`,`"prevunique"` —— 同上,但是跳过键相同的记录 (仅适用于索引上的光标,例如,对于价格为5的书,仅返回第一本)。 + - `"nextunique"`,`"prevunique"` —— 同上,但是跳过键相同的记录 (仅适用于索引上的光标,例如,对于价格为 5 的书,仅返回第一本)。 **光标对象的主要区别在于 `request.onSuccess` 多次触发:每个结果触发一次。** @@ -744,11 +744,11 @@ try { window.addEventListener('unhandledrejection', event => { let request = event.target; // IndexedDB 本机请求对象 let error = event.reason; // 未处理的错误对象,与 request.error 相同 - // ...报告错误... + // ……报告错误…… }); ``` -### “非活跃交易”陷阱 +### “非活跃事务”陷阱 我们都知道,浏览器一旦执行完成当前的代码和**微任务**之后,事务就会自动提交。因此,如果我们在事务中间放置一个类似 `fetch` 的宏任务,事务只是会自动提交,而不会等待它执行完成。因此,下一个请求会失败。 @@ -769,7 +769,7 @@ await inventory.add({ id: 'js', price: 10, created: new Date() }); // 错误 `fetch` `(*)` 后的下一个 `inventory.add` 失败,出现“非活动事务”错误,因为这时事务已经被提交并且关闭了。 -解决方法与使用本机 IndexedDB 时相同:进行新事务,或者将事物分开。 +解决方法与使用本机 IndexedDB 时相同:进行新事务,或者将事情分开。 1. 准备数据,先获取所有需要的信息。 2. 然后保存在数据库中。 @@ -788,7 +788,7 @@ let promise = books.add(book); // 获取 promise 对象(不要 await 结果) let request = promise.request; // 本地请求对象 let transaction = request.transaction; // 本地事务对象 -// ...做些本地的 IndexedDB 的处理... +// ……做些本地的 IndexedDB 的处理…… let result = await promise; // 如果仍然需要 ``` From f9d101580b3e0b00261b4ee0c40b620e2e44c510 Mon Sep 17 00:00:00 2001 From: LeviDing Date: Sat, 18 Apr 2020 15:05:30 +0800 Subject: [PATCH 5/7] Update indexeddb-structure.svg --- .../03-indexeddb/indexeddb-structure.svg | 83 +------------------ 1 file changed, 1 insertion(+), 82 deletions(-) diff --git a/6-data-storage/03-indexeddb/indexeddb-structure.svg b/6-data-storage/03-indexeddb/indexeddb-structure.svg index 9fc1eb6ecd..c65607e06e 100644 --- a/6-data-storage/03-indexeddb/indexeddb-structure.svg +++ b/6-data-storage/03-indexeddb/indexeddb-structure.svg @@ -1,82 +1 @@ - - - - indexeddb-structure.svg - Created with sketchtool. - - - - - key1: value1 - - - - - - - Database - - - objectStore - - - - - - - - objectStore - - - - - - key3: value3 - - - key2: value2 - - - key4: value4 - - - key5: value5 - - - - - objectStore - - - key1: value1 - - - key3: value3 - - - key2: value2 - - - key4: value4 - - - key5: value5 - - - key1: value1 - - - key3: value3 - - - key2: value2 - - - key4: value4 - - - key5: value5 - - - - \ No newline at end of file +key1: value1DatabaseobjectStoreobjectStorekey3: value3key2: value2key4: value4key5: value5objectStorekey1: value1key3: value3key2: value2key4: value4key5: value5key1: value1key3: value3key2: value2key4: value4key5: value5 From f2af7667bfae86e1cc9f9862d025caaddce9747c Mon Sep 17 00:00:00 2001 From: LeviDing Date: Sat, 18 Apr 2020 15:06:43 +0800 Subject: [PATCH 6/7] Update indexeddb-structure.svg From f37326aa725d2a2d49c09a9a53e1eaa187364e7a Mon Sep 17 00:00:00 2001 From: LeviDing Date: Sat, 18 Apr 2020 15:07:44 +0800 Subject: [PATCH 7/7] Delete indexeddb-structure.svg --- 6-data-storage/03-indexeddb/indexeddb-structure.svg | 1 - 1 file changed, 1 deletion(-) delete mode 100644 6-data-storage/03-indexeddb/indexeddb-structure.svg diff --git a/6-data-storage/03-indexeddb/indexeddb-structure.svg b/6-data-storage/03-indexeddb/indexeddb-structure.svg deleted file mode 100644 index c65607e06e..0000000000 --- a/6-data-storage/03-indexeddb/indexeddb-structure.svg +++ /dev/null @@ -1 +0,0 @@ -key1: value1DatabaseobjectStoreobjectStorekey3: value3key2: value2key4: value4key5: value5objectStorekey1: value1key3: value3key2: value2key4: value4key5: value5key1: value1key3: value3key2: value2key4: value4key5: value5