文章源码:https://github.com/Haixiang6123/my-idb-keyval
预览链接:http://yanhaixiang.com/my-idb-keyval/
参考轮子:https://github.com/jakearchibald/idb-keyval
你真的会使用 indexdb 么
相信不少人看过阮一峰的 《浏览器数据库 IndexedDB 入门教程》。我自己的感觉是依然不会使用 indexedDB,感觉每一步操作都很简单但是就是不会把整个流程跑通。
正好最近用到了 idb-keyval 这个库,阅读了一下源码后终于是有点感觉了。下来就从一个简单的例子开始,一步步来造一个 idb-keyval 库吧。
一个简单的需求
我们都知道 localStorage 的用法,现在就用 indexedDB 来实现 localStorage 的用法。
丑陋的实现
根据阮一峰老师的教程,假如我们要实现 getItem
方法,用最最最原生的方法就是:
const dbName = 'key-val'
const storeName = 'keyval'
export function uglyGet(key: string) {
// 打开数据库
const openDBRequest = indexedDB.open(dbName)
// 创建表
openDBRequest.onupgradeneeded = function () {
openDBRequest.result.createObjectStore(storeName)
}
// 失败回调
openDBRequest.onerror = () => console.log('出错啦')
// 成功回调
openDBRequest.onsuccess = () => {
// 获取数据库
const db = openDBRequest.result
// 获取数据库里的 store
const store = db.transaction(storeName, 'readonly').objectStore(storeName)
// 获取值操作
const getRequest = store.get(key);
getRequest.onsuccess = function() {
// 获取到值
console.log(`获取 ${
key} 成功`, this.result)
}
getRequest.onerror = function() {
console.log(`获取 ${
key} 失败`)
}
}
}
上面做了以下操作:
- 打开 key-val 数据库
- 添加 keyval 对象仓库(如果没有的话)
- 获取 key 对应的 value 值,并显示
this.result
看看看看,现在取个 value 还有没有点规矩了?
咱们的诉求是什么呀?是通过一个简单的 API 来获取一个 value,里面的逻辑应该只要调几个接口就够了。你让我造这么多个回调和监听,只为拿一个 value 值。丢不丢人?恶不恶心?难看不难看呐?
下面就来一步一步改造上面的代码。
promisify
看到回调,很容易就想到了利用 Promise 来进行封装,封装之后就可以用 await-async 来写代码了,避免回调地狱。上面主要是 request 来执行一些操作,所以我们应该将这些操作进行 promisify:
/**
* 将 request 变为 Promise 对象
* indexeddb 操作成功后会调用 onsuccess,因此绑定到 resolve
* indexeddb 操作失败后会调用 onerror,因此绑定到 reject
* @param request
*/
export function promisifyRequest<T = undefined>(request: IDBRequest<T> | IDBTransaction): Promise<T> {
return new Promise<T>((resolve, reject) => {
// @ts-ignore
request.onsuccess = () => resolve(request.result)
// @ts-ignore
request.onerror = () => reject(request.error)
});
}
然后可以改写成 promise.then 的写法:
export async function uglyGet(key) {
// 打开数据库
const openDBRequest = indexedDB.open(dbName)
// 创建表
openDBRequest.onupgradeneeded = () => openDBRequest.result.createObjectStore(storeName)
// 失败回调
return promisifyRequest(openDBRequest)
.then(db => {
// 获取数据库里的 store
const store = db.transaction(storeName