欢迎来到我关于使用 IndexedDB 的包装库系列的第三篇,也是最后一篇(目前)。我在本月初开始这个系列,演示了一个用 IndexedDB 实现的简单的联系人数据库。在第二篇文章中,我演示了Dexie库如何使 IndexedDB 的工作变得更加简单。今天我将看看我在这个系列中的最后一个 "计划 "条目(如果我发现更多的话,我可能会再次重温这个条目)--使用DPP,或者说JavaScript的深度持久化代理对象。
DPP利用了JavaScript的代理对象功能。这是一个低级别的功能,可以让你控制如何提供对一个对象的访问。我想说这是 "新的",但它已经被讨论了一段时间了。它得到了很大的支持,所以使用起来很安全,但说实话,它感觉更像是为库/框架开发者量身定做的东西,而不是 "日常 "编码。当然,这正是DPP所涵盖的情况--在IndexedDB之上的一个库。
DPP 是如何工作的?你从一个异步调用开始,初始化你的对象。最简单的例子是这样的:
let dpp = await createDPP({ storeName:'MyDataStore' });
这将在一个名为DPP 的默认 IndexedDB 数据库下创建一个存储,MyDataStore 。你也可以指定一个自定义的名字:
let dpp = await createDPP({ storeName:'MyDataStore', idb_name:'MyDB' });
一旦你初始化了 DPP,你就会像这样询问数据:
let myObj = await dpp.start();
为了清楚起见,这里有一个 "完整 "的例子:
let dpp = await createDPP({ storeName:'MyDataStore' });
let myObj = await dpp.start();
在这一点上,myObj 是持久化的。那是什么意思?我可以做:
myObj.purpose = "to store crap";
myObj.lastStore = new Date();
然后...我已经完成了。就这样了。有两个键的对象将被持久化,下次我再处理它时,我就可以直接使用它。比如说:
if(myObj.purpose) {
console.log(`My purpose is ${myObj.purpose}`);
} else myObj.purpose = "to store crap";
注意,这都是通过常规的同步调用完成的。库在幕后使用网络工作者处理一切。(你可以在项目的readmedocs上得到更多的细节)。
那么,这对我们的示例应用程序有什么影响呢?和以前一样,我将跳过讨论DOM的东西,只关注与持久性有关的部分。
包括DPP
包括DPP需要使用一个导入语句。老实说,在客户端JavaScript中,这仍然有点让我困惑。我需要学习更多关于它的知识:
const {createDPP} = await import('https://cdn.jsdelivr.net/gh/robtweed/DPP/src/dpp_browser.min.js');
初始化数据库
初始化数据库只是使用我上面展示的两行。结果是一个对象,而不是一个数据库,所以我把initDb 改为initContacts 。所以在我的init 函数中,我已经得到了:
contactsOb = await initContacts();
而initContacts 是:
async function initContacts() {
const {createDPP} = await import('https://cdn.jsdelivr.net/gh/robtweed/DPP/src/dpp_browser.min.js');
let dpp = await createDPP({
storeName: 'contacts_dpp'
});
let contactsOb = await dpp.start();
if(!contactsOb.contacts) contactsOb.contacts = [];
return contactsOb;
}
注意我检查了主对象下的contacts 。我正在处理一个数组的数据,所以我打算把它存储为contactsOb.contacts 。
处理数据
现在是有趣的部分。在我之前的两篇博文中,我为处理DOM的每一种用途设置了一个函数,比如渲染联系人、编辑等,还有一个函数用于持久化。这使得从 "纯IndexedDB "切换到Dexie更容易。然而,现在所有这些都没有了。我没有做持久化工作,DPP在做。
因此,例如,我不需要一个函数来获取联系人。我已经有了它。当我想渲染他们的时候,我只是做:
contactsOb.contacts.forEach((c,i) => {
如果我想保存一个联系人,我要么通过它的索引更新一个现有的联系人,要么把它添加到数组的末尾:
if($key.value) {
let idx = parseInt($key.value,10);
contactsOb.contacts[idx] = contact;
} else contactsOb.contacts.push(contact);
删除一个联系人只需调用splice:
let key = parseInt(e.target.dataset.key,10);
contactsOb.contacts.splice(key, 1);
然后...我就完成了。说实话,DPP有多酷令人震惊。我的原始版本有181行的JavaScript。我的Dexie版本将其减少到106行。DPP版本下降得更多,为97行。虽然没有那么戏剧性的下降,但其中没有一行IndexedDB代码,因为它都是由库处理的。下面是完整的版本供你参考。