原文作者:Chibuike Nwachukwu
原文地址:https://blog.logrocket.com/using-indexeddb-complete-guide/
翻译:一川
1写在前面
数据存储是大多数 Web
应用程序的重要组成部分,从跟踪用户数据到应用程序数据。随着更快、更强大的Web
应用程序的快速开发,需要高效的客户端存储来帮助开发。
多年来,Web
上的客户端存储已经发生了很大的变化,从用于存储用户数据的 cookie
到 WebSQL
(目前已弃用)的出现,它允许开发人员将数据存储在浏览器中的 SQL
数据库中,进而允许熟悉SQL
的用户轻松构建健壮的应用程序。
IndexedDB
是WebSQL
的替代品,提供比以前的同类产品更多的存储容量。在本教程中,我们将探讨如何使用和设置 IndexedDB
进行 Web
应用程序数据存储,以及如何使用可用的 API
操作其数据。
2什么是 IndexedDB
?
IndexedDB
是用于客户端存储的低级 API
。它是一个成熟的、持久的 NoSQL
存储系统,可在浏览器中使用,允许存储不同类型的数据,例如:
- 文件或 Blob
- 图片和视频
- 结构化数据,如对象、列表和数组
IndexedDB
可用于各种场景,例如缓存、PWA
、游戏等,并且还支持事务。它的开发是为了有效地满足 Web
应用程序的多种需求。
3设置我们的项目
我们不会做任何花哨的设置,因为IndexedDB
在网络上本地运行。首先,创建一个新目录来容纳项目:
mkdir indexed-db && cd indexed-db
现在,我们将创建一个文件来查看我们的应用程序,创建一个index.js
脚本 index.html
文件来存储我们的应用程序逻辑:
touch index.html index.js styles.css
4将数据保存到索引数据库
若要了解使用此数据库的好处并了解如何与 API
交互,我们将创建一个基本的待办事项应用程序。我们启用一个添加功能以查看如何将数据保存到数据库,另一个功能用于查看所有待办事项,以及一个删除功能以查看 API
的 GET
和 DELETE
函数。
打开在上一节中创建的,index.html
并添加以下代码:
TODO APP
TODO APP
TODOs
Add Todo
Todo title
Todo description
Save
这将创建我们的 Web
应用程序的基础结构。我们在这里做两件主要的事情:首先,我们创建一个部分来显示/查看保存在数据库中的所有待办事项,其次,我们创建一个用于向数据库添加待办事项的部分。
我们还向应用程序添加一些基本样式。打开styles.css
文件并添加以下内容:
html {
font-family: sans-serif;
}
body {
margin: 0 auto;
max-width: 800px;
}
header, footer {
background-color: blue;
color: white;
padding: 0 20px;
}
.add, .view {
padding: 30px;
width: 40%;
}
.add {
background: #ebe6e6;
}
section {
padding: 10px;
background: #3182d4;
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
}
h1 {
margin: 0;
}
ol {
list-style-type: none;
}
div {
margin-bottom: 10px;
}
该文件 index.js
是应用程序的核心,因为它包含用于在应用程序和 IndexedDB
之间进行交互的逻辑。
首先,我们需要创建数据库;然后,我们可以通过创建一个对象存储(类似于 SQL
中的表)来初始化它,我们将使用它来存储每个项目的详细信息。打开 index.js
该文件并向其添加以下逻辑:
let db;
const openOrCreateDB = window.indexedDB.open('todo_db', 1);
openOrCreateDB.addEventListener('error', () => console.error('Error opening DB'));
openOrCreateDB.addEventListener('success', () => {
console.log('Successfully opened DB');
db = openOrCreateDB.result;
});
openOrCreateDB.addEventListener('upgradeneeded', init => {
db = init.target.result;
db.onerror = () => {
console.error('Error loading database.');
};
const table = db.createObjectStore('todo_tb', { keyPath: 'id', autoIncrement:true });
table.createIndex('title', 'title', { unique: false });
table.createIndex('desc', 'desc', { unique: false });
});
如上所示,创建了一个名为的数据库,然后创建了一个名为 todo_db
的对象存储,其中包含两个索引, title
以及desc
。这些索引允许在存储中复制其值,这类似于在 SQL
中创建表,然后创建两列。
接下来,要添加 Save
功能,我们继续检索输入到表单中的值,然后将它们保存到数据库中:
const todos = document.querySelector('ol');
const form = document.querySelector('form');
const todoTitle = document.querySelector('#title');
const todoDesc = document.querySelector('#desc');
const submit = document.querySelector('button');
form.addEventListener('submit', addTodo);
function addTodo(e) {
e.preventDefault();
const newTodo = { title: todoTitle.value, body: todoDesc.value };
const transaction = db.transaction(['todo_tb'], 'readwrite');
const objectStore = transaction.objectStore('todo_tb');
const query = objectStore.add(newTodo);
query.addEventListener('success', () => {
todoTitle.value = '';
todoDesc.value = '';
});
transaction.addEventListener('complete', () => {
showTodos();
});
transaction.addEventListener('error', () => console.log('Transaction error'));
}
将值添加到存储后,将清空两个表单字段,以便用户可以在其列表中输入新项目。我们可以通过调用 showTodos
方法来更新视图,我们将在下一节中看到。
5检索和显示数据
要确认保存待办事项功能是否有效,请打开并使用浏览器的检查功能。在Chrome
中,您可以在“应用程序”选项卡下的“存储”中看到 IndexedDB
。如下图所示,我们创建了数据库并将第一个待办事项保存到对象存储中 todo_tb
:
为了在用户加载页面时显示可用的待办事项,并提供以前添加和删除的待办事项的视图,我们将创建一个名为 showTodos
的方法:
function showTodos() {
while (todos.firstChild) {
todos.removeChild(todos.firstChild);
}
const objectStore = db.transaction('todo_tb').objectStore('todo_tb');
objectStore.openCursor().addEventListener('success', e => {
const pointer = e.target.result;
if(pointer) {
const listItem = document.createElement('li');
const h3 = document.createElement('h3');
const pg = document.createElement('p');
listItem.appendChild(h3);
listItem.appendChild(pg);
todos.appendChild(listItem);
h3.textContent = pointer.value.title;
pg.textContent = pointer.value.body;
listItem.setAttribute('data-id', pointer.value.id);
const deleteBtn = document.createElement('button');
listItem.appendChild(deleteBtn);
deleteBtn.textContent = 'Remove';
deleteBtn.addEventListener('click', deleteItem);
pointer.continue();
} else {
if(!todos.firstChild) {
const listItem = document.createElement('li');
listItem.textContent = 'No Todo.'
todos.appendChild(listItem);
}
console.log('Todos all shown');
}
});
}
此方法从存储中获取待办事项,循环遍历每个项目,并为每个项目创建一个 HTML
元素。它将项目追加到网页上的ol
列表元素,并将每个待办事项 id
的 传递给名为 data-id
的数据属性。稍后,当我们介绍该 deleteItem
函数时,我们将使用此唯一ID
来标识每个待办事项,当我们需要将其从存储中删除时。
若要在页面加载时获取待办事项,请将 openOrCreateDB
成功事件侦听器修改为:
openOrCreateDB.addEventListener('success', () => {
console.log('Successfully opened DB');
db = openOrCreateDB.result;
showTodos();
});
6从数据库中删除数据
最后,让我们测试此数据库的 DELETE API
,并为我们的待办事项列表应用创建一个 Delete
函数:
function deleteItem(e) {
const todoId = Number(e.target.parentNode.getAttribute('data-id'));
const transaction = db.transaction(['todo_tb'], 'readwrite');
const objectStore = transaction.objectStore('todo_tb');
objectStore.delete(todoId);
transaction.addEventListener('complete', () => {
e.target.parentNode.parentNode.removeChild(e.target.parentNode);
alert(`Todo with id of ${todoId} deleted`)
console.log(`Todo:${todoId} deleted.`);
if(!todos.firstChild) {
const listItem = document.createElement('li');
listItem.textContent = 'No Todo.';
todos.appendChild(listItem);
}
});
transaction.addEventListener('error', () => console.log('Transaction error'));
}
这会使用传递给方法的唯一ID
删除特定的待办事项,并从网页中删除该元素。删除存储中的最后一个待办事项后,它会在待办事项列表的位置显示“无待办事项”消息。
要确认待办事项已从数据库中删除,请继续检查网页并单击应用程序选项卡。可以看出,todo_tb
对象存储现在不包含任何项目:
最终的 Web
应用程序如下所示:
7递增索引数据库版本
IndexedDB 还允许开发人员递增数据库版本。打开数据库时,请指定所需的版本号:
window.indexedDB.open('todo_db', 1);
如果数据库不存在,则将使用指定的版本创建该数据库。如果数据库已存在,则检查版本号。
如果在open
方法调用期间指定的版本号高于现有版本,则会通过该onUpgradeNeeded
事件触发版本更改事件。此事件允许您执行数据库架构更改或数据迁移。
这里需要注意的一点是,删除以前的对象存储以添加新选项,创建新存储时也会删除旧存储中的所有其他数据。在升级数据库之前,请注意读出旧内容并将其保存在其他位置。
8使用索引数据库的缺点
由于IndexedDB
依赖于客户端的 Web
浏览器,因此它通常更适合个人用户或小规模的应用程序。尽管它可以处理大量数据,但在大型应用程序或多人使用的应用程序中使用 IndexedDB
时,需要牢记某些注意事项。
可伸缩性限制
IndexedDB
在Web
浏览器中运行,这意味着它仅限于客户端环境的功能和资源。对于需要处理大量并发用户或极高吞吐量的方案,它可能无法很好地缩放。
数据大小限制
不同的Web
浏览器对IndexedDB
中可以存储的最大数据量施加了限制。这些限制因浏览器而异,范围从几兆字节到几百兆字节不等。
了解这些限制并相应地设计应用程序非常重要。数据存储用完后,您将无法在数据库中存储新数据,因为会触发 QuotaExceededError
错误。
同步挑战
IndexedDB
不提供内置机制来处理客户端之间的数据同步或处理分布式环境中的冲突。您需要实现自定义同步逻辑来处理这些方案。在应用程序的不同实例之间维护数据一致性和同步变得复杂。
因此,对于较大规模的应用程序或多人使用的应用程序,使用服务器端数据库或基于云的存储解决方案更有效。
9写在最后
在本文中,我们了解了 IndexedDB
,一个 Web
上的数据库,以及如何使用 JavaScript
与它交互以存储 Web
应用程序数据。