在昨天的文章中,我谈到了我对再次挖掘 IndexedDB 的兴趣,特别是,我想研究一些使使用该功能更容易的库。在第一篇文章中,我描述了一个基本的 "联系人 "应用程序,并演示了使用 IndexedDB 添加持久性所需的代码。今天,我将更新我的应用程序,以利用一个名为Dexie 的包装库。
Dexie 是 IndexedDB 的一个非常简单的封装器,并且对包括 Vue、React、Svelte 和 Angular 在内的框架有反应式支持。Dexie是一个免费的库,但他们也出售一种叫做Dexie Cloud的商业同步服务。它使用了Promises,这使得它与async/await的使用更加简单。我建议你看一下广泛的文档,因为我将只关注我的应用程序转换所需的部分。
和以前一样,我将跳过讨论DOM的东西,只关注与持久性有关的部分。
包括Dexie
包括Dexie就像添加一个脚本ag和指向CDN一样简单,unpkg.com/dexie/dist/…
初始化数据库
还记得我说过与 Dexie 合作使 IndexedDB 变得简单吗?没有什么能比初始化更说明问题了。为了便于比较,这里是原始代码:
async function initDb() {
return new Promise((resolve, reject) => {
let request = indexedDB.open('contacts', 1);
request.onerror = event => {
alert('Error Event, check console');
console.error(event);
}
request.onupgradeneeded = event => {
console.log('idb onupgradeneeded firing');
let db = event.target.result;
let objectStore = db.createObjectStore('contacts', { keyPath: 'id', autoIncrement:true });
objectStore.createIndex('lastname', 'lastname', { unique: false });
};
request.onsuccess = event => {
resolve(event.target.result);
};
});
}
记住,你必须打开数据库,然后监听一个升级事件(这个事件在第一次访问时也被触发),并在那里做任何结构性设置。这涉及到创建存储和索引。我的演示没有做任何搜索,所以我不用担心这个问题。
这里是Dexie的版本:
async function initDb() {
let db = new Dexie('contacts_dexie');
db.version(1).stores({contacts:'++id'})
return db;
}
这真是令人震惊的小。公平地说,这实际上并没有创建数据库,它只是 "准备 "了你的网页。Dexie很聪明地推迟了做任何事情,直到你试图处理数据。但需要注意的是,如果你打开了devtools,并且正在查看你的数据库,你一开始就不会看到任何东西。
第二行定义了一个名为contacts 的存储。这个字符串值是你定义主键和索引的方式。因为我没有任何索引只有一个主键,所以它比较短。如果我想在姓氏属性上建立一个索引,它看起来会是这样的:
db.version(1).stores({contacts:'++id,lastName'})
简单的事情还能继续吗?
获取所有联系人
接下来是获取所有联系人的调用。这里是原始的:
async function getContacts() {
return new Promise((resolve, reject) => {
let transaction = db.transaction(['contacts'], 'readonly');
transaction.onerror = event => {
reject(event);
};
let store = transaction.objectStore('contacts');
store.getAll().onsuccess = event => {
resolve(event.target.result);
};
});
}
而现在是Dexie版本。
async function getContacts() {
return await db.contacts.toArray();
}
如果你还没有印象,我不知道什么会让你印象深刻
获取一个联系人
再一次,以前的版本:
async function getContact(key) {
return new Promise((resolve, reject) => {
let transaction = db.transaction(['contacts'], 'readonly');
transaction.onerror = event => {
reject(event);
};
let store = transaction.objectStore('contacts');
store.get(key).onsuccess = event => {
resolve(event.target.result);
};
});
}
而用Dexie:
async function getContact(key) {
return await db.contacts.get(key);
}
保存联系人
如果你还记得,在以前的版本中,我们使用put API,让我们有一种方法来存储新的联系人以及更新现有的联系人。这里是原版:
async function persistContact(contact) {
return new Promise((resolve, reject) => {
let transaction = db.transaction(['contacts'], 'readwrite');
transaction.oncomplete = event => {
resolve();
};
transaction.onerror = event => {
reject(event);
};
let store = transaction.objectStore('contacts');
store.put(contact);
});
}
然后是Dexie:
async function persistContact(contact) {
await db.contacts.put(contact);
}
删除联系人
最后,让我们看看删除联系人的情况。这里又是原文:
async function removeContact(key) {
return new Promise((resolve, reject) => {
let transaction = db.transaction(['contacts'], 'readwrite');
transaction.oncomplete = event => {
resolve();
};
transaction.onerror = event => {
reject(event);
};
let store = transaction.objectStore('contacts');
store.delete(key);
});
}
你可以猜一猜它在Dexie里有多少行:
async function removeContact(key) {
return await db.contacts.delete(key);
}
全部内容
下面你会看到完整的应用程序。说实话,我几乎考虑过用Dexie这么简单的方法来删除我的持久化方法。但我也认为有一个处理DOM事件的方法和一个只专注于持久化的方法是很好的。这些函数可能只有一到两行,但这样的抽象化使我能够在以后进行架构上的改变。下面是最终的应用程序,再次原谅嵌入中略显混乱的格式。