IndexedDBStorage
2025年01月19日
一、认识
IndexedDBStorage
用于与 LocalStorage
一样的功能: 支持有效期, 数据存储时可以设置有效期(以毫秒为单位); 存储:记录过期时间戳; 读取:检查时间戳,过期则返回 null 并删除数据; 懒删除: 过期数据仅在读取时被清理,避免对性能产生影响; 保持标准 API
, 提供 setItem
、getItem
、removeItem
、clear
、keys
等方法,接口清晰且易于使用; 而且支持大容量存储: 依托 IndexedDB
的优势,支持存储更大规模的数据,同时具备结构化存储能力。不过, IndexedDB
的存储是异步存储, LocalStorage
是同步的。
二、实现
class EnhancedIndexedDBStorage {
constructor(dbName = 'appDB', storeName = 'keyValueStore') {
this.dbName = dbName; // 数据库名称
this.storeName = storeName; // 存储对象名称
this.dbPromise = this.initDB();
}
// 初始化数据库
initDB() {
return new Promise((resolve, reject) => {
const request = indexedDB.open(this.dbName, 1);
request.onupgradeneeded = (event) => {
const db = event.target.result;
if (!db.objectStoreNames.contains(this.storeName)) {
db.createObjectStore(this.storeName, { keyPath: 'key' }); // 创建存储对象
}
};
request.onsuccess = (event) => resolve(event.target.result);
request.onerror = (event) => reject(event.target.error);
});
}
// 获取存储对象
async getStore(mode = 'readonly') {
const db = await this.dbPromise;
const transaction = db.transaction(this.storeName, mode);
return transaction.objectStore(this.storeName);
}
/**
* 设置数据,支持有效期
* @param {string} key 键名
* @param {*} value 要存储的值
* @param {number} expiry 有效期(毫秒),可选
*/
async setItem(key, value, expiry = null) {
const store = await this.getStore('readwrite');
const data = {
value,
expiry: expiry ? Date.now() + expiry : null, // 计算过期时间
};
return new Promise((resolve, reject) => {
const request = store.put({ key, ...data });
request.onsuccess = () => resolve();
request.onerror = (event) => reject(event.target.error);
});
}
/**
* 获取数据,检查有效期
* @param {string} key 键名
* @returns {*} 数据或 null(如果过期或不存在)
*/
async getItem(key) {
const store = await this.getStore();
return new Promise((resolve, reject) => {
const request = store.get(key);
request.onsuccess = (event) => {
const result = event.target.result;
if (!result) {
resolve(null);
return;
}
const { value, expiry } = result;
if (expiry && Date.now() > expiry) {
// 数据过期,删除该项
this.removeItem(key);
resolve(null);
} else {
resolve(value);
}
};
request.onerror = (event) => reject(event.target.error);
});
}
/**
* 删除数据
* @param {string} key 键名
*/
async removeItem(key) {
const store = await this.getStore('readwrite');
return new Promise((resolve, reject) => {
const request = store.delete(key);
request.onsuccess = () => resolve();
request.onerror = (event) => reject(event.target.error);
});
}
/**
* 清空所有数据
*/
async clear() {
const store = await this.getStore('readwrite');
return new Promise((resolve, reject) => {
const request = store.clear();
request.onsuccess = () => resolve();
request.onerror = (event) => reject(event.target.error);
});
}
/**
* 获取所有键
* @returns {Promise<string[]>} 所有键的数组
*/
async keys() {
const store = await this.getStore();
return new Promise((resolve, reject) => {
const request = store.getAllKeys();
request.onsuccess = (event) => resolve(event.target.result);
request.onerror = (event) => reject(event.target.error);
});
}
/**
* 获取键值对数量
* @returns {Promise<number>} 键值对数量
*/
async length() {
const keys = await this.keys();
return keys.length;
}
}
// 示例用法
(async () => {
const storage = new EnhancedIndexedDBStorage('myAppDB', 'myStore');
// 设置值,5 秒后过期
await storage.setItem('user', { name: 'Alice', age: 30 }, 5000);
await storage.setItem('token', 'abcdef', 10000);
// 立即读取
console.log(await storage.getItem('user')); // { name: 'Alice', age: 30 }
// 6 秒后读取
setTimeout(async () => {
console.log(await storage.getItem('user')); // null(过期自动清理)
console.log(await storage.getItem('token')); // 'abcdef'
}, 6000);
// 清空存储
await storage.clear();
console.log(await storage.keys()); // []
})();