使用indexedDB 模拟实现一个基础的 localStorage
进入正文之前我们先来了解一下,indexedDB的几个基础操作。
IDBOpenDBRequest
:IndexedDB API
的接口使用特定的事件处理程序属性,提供对打开或删除数据库(使用IDBFactory.open
和执行IDBFactory.deleteDatabase
)的请求结果的访问。
我们可以通过 indexedDB.open(name,version)
打开一个数据库:
name
数据库的名称。
version
(可选)
用于打开数据库的版本。如果未提供版本且数据库存在,则将打开与数据库的连接而不更改其版本。如果未提供版本,并且数据库不存在,则将使用version
创建它1。
var request = window.indexedDB.open("store", 1);
request.upgradeneeded
:初始化或version
版本需要更新的时候触发。
我们可以在upgradeneeded
函数监听初始化状态,并创建数据库IDBDatabase
:
request.onupgradeneeded = event => {
IDBDatabase = event.target.result;
objectStore = IDBDatabase.createObjectStore(name, options);
objectStore.createIndex(indexName, keyPath, objectParameters);
};
下面我们来解析一下代码:
创建一个对象存储库(新建表)。
objectStore = IDBDatabase.createObjectStore(name, options);
name
要创建的新对象库的名称。可以使用空名称创建对象存储。
optionalParameters
(可选)
keyPath
,主键(key
)是默认建立索引的属性。
autoIncrement
,如果是true
,则对象存储库具有密钥生成器。默认为false
。
创建一个新索引。
objectStore.createIndex(indexName, keyPath, objectParameters);
indexName
要创建的索引的名称。可以使用空名称创建索引。
keyPath
要使用的索引的主键。请注意,可以创建一个带有空值的索引keyPath
,也可以将序列(数组)作为keyPath
。
optionalParameters
(可选)
unique
:如果为true
,则索引将不允许单个键重复值。
request.onsuccess
:IDBRequest
成功时触发。
我们可以在onsuccess
监听函数里面,更新数据库IDBDatabase
,下面对于IDB
的操作都会基于此IDBDatabase
IDBDatabase = event.target.result;
IDBTransaction
:操作数据表的读写。
IDBTransaction接口使用事件处理程序属性在数据库上提供了静态的异步事务。所有数据的读取和写入都在事务内完成。
// IDBDatabase.transaction("store", "readwrite");
IDBDatabase.transaction(name, mode);
name
请求的对象库的名称。
mode
设置对象存储库的模式。默认值为readonly
。
IDBTransaction.objectStore()
。
返回一个IDBObjectStore表示对象存储库的对象,该对象存储库属于此事务的范围。
// IDBTransaction.objectStore("store");
IDBTransaction.objectStore(name);
name
请求的对象库的名称。
接下来我们了解下IDB的增删查改
IDBObjectStore.add()
:新记录添加到对象存储中。IDBObjectStore.clear()
:删除所有当前记录。IDBObjectStore.delete()
:删除单个记录。IDBObjectStore.get()
:从对象存储中检索特定记录。IDBObjectStore.put()
:更新对象存储中的现有记录。IDBObjectStore.openCursor()
:遍历对象存储中的所有记录。
index.html,添加一个触发按钮
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
</head>
<body>
<button>set</button>
</body>
</html>
<script src="./localStorageMock.js"></script>
localStorageMock.js
const name = Symbol('localStorageMock');
const localStorageMock = new (class {
constructor() {
Object.assign(this, { [name]: {}, db: {}, objectStore: {}, result: '' });
Object.defineProperties(this, {
DBOpenRequest: {
value: window.indexedDB.open('localStorageMock', 1),
writable: false,
configurable: false,
},
});
this.DBOpenRequest.onupgradeneeded = event => {
this.db = event.target.result;
if (!this.db.objectStoreNames.contains('store')) {
this.objectStore = this.db.createObjectStore('store', { keyPath: 'name' });
this.objectStore.createIndex('name', 'name', { unique: false });
}
};
this.DBOpenRequest.onerror = event => console.error(event);
this.DBOpenRequest.onsuccess = event => {
this.db = event.target.result;
};
}
/** 获取所有Storage数据列表 */
readAll() {
return new Promise(resolve => {
let objectStore = this.db.transaction('store').objectStore('store');
let arr = [];
objectStore.openCursor().onsuccess = event => {
let cursor = event.target.result;
if (cursor) {
arr.push(cursor.value);
cursor.continue();
return resolve(arr);
} else {
console.log('没有更多数据了!');
}
};
});
}
getItem(key) {
return new Promise((resolve, reject) => {
let request = this.db
.transaction(['store'], 'readonly')
.objectStore('store')
.index('name')
.get(key);
request.onerror = () => reject('事务失败');
request.onsuccess = function(event) {
if (request.result) {
return resolve(request.result);
} else {
return reject('未获得数据记录');
}
};
});
}
/** 注意隐患,刷新会情况this[name]数据,建议直接setItem */
add(key, value) {
console.log(this[name].hasOwnProperty(key));
if (this[name].hasOwnProperty(key)) {
return this.setItem(key, value);
}
Object.assign(this[name], { [key]: value });
let request = this.db
.transaction(['store'], 'readwrite')
.objectStore('store')
.add({ name: key, value: value });
request.onsuccess = event => console.log('数据写入成功', event.target.result);
request.onerror = err => console.error('数据写入失败', err.target.error);
}
removeItem(key) {
let request = this.db
.transaction(['store'], 'readwrite')
.objectStore('store')
.delete(key);
request.onsuccess = () => console.log('数据删除成功');
request.onerror = err => console.error('数据删除失败', err);
}
clear() {
let request = this.db
.transaction(['store'], 'readwrite')
.objectStore('store')
.clear();
request.onsuccess = () => console.log('数据库清除成功');
request.onerror = err => console.error('数据库清除失败', err);
}
setItem(key, value) {
let request = this.db
.transaction(['store'], 'readwrite')
.objectStore('store')
.put({ name: key, value: value });
request.onsuccess = () => console.log('数据写入成功');
request.onerror = err => console.error('数据写入失败', err);
}
})();
document.getElementsByTagName('button')[0].addEventListener('click', async function() {
console.log(localStorageMock);
await localStorageMock.setItem('小明', '22岁');
console.log(await localStorageMock.getItem('小明'));
console.log(await localStorageMock.readAll());
await localStorageMock.removeItem('小明');
await localStorageMock.clear();
});