• 作者:老汪软件技巧
  • 发表时间:2024-10-10 11:04
  • 浏览量:

介绍

浏览器本地存储API有很多,例如:Cookie、IndexDB、WebStorage 他们都有各自的缺点,我们希望设计一套通用的本地存储API去解决这个问题。

首先我们要知道每个本地存储API的缺点:

预期的结果:

基于以上的问题能够使用的API只有 indexDB 和 localStorage,我们希望可以在现代浏览器中使用 indexDB 在老版本浏览器中使用 localStorage 进行存储数据,

遇到的问题:

它们的存储方式是不同的,例如:indexDB 需要调用 open 方法开启数据库后才开始存储操作并且所有的操作都是基于异步的,而 localStorage 只需要使用 getItem 、setItem等方法直接同步存储。

如何解决:

我们可以通过模板模式设置一个基类内部包含 getItem、setItem等方法并且要求所有操作都是异步的,让 indexDB 和 localStorage 都继承该基类去解决问题。

代码实现

设置模板:

class Template {
    getItem(key){
        throw new Error('必须实现 getItem 方法');
    },
    setItem(key,value){
        throw new Error('必须实现 setItem 方法');
    },
    removeItem(key){
        throw new Error('必须实现 removeItem 方法');
    },
    clear(){
        throw new Error('必须实现 clear 方法');
    },
    getAll(){
        throw new Error('必须实现 getAll 方法');
    }
}

实现LocalStorage类:

class LocalStorage {
    getItem(key) {
        return new Promise((resolve, reject) => {
            const result = localStorage.getItem(key);
            try {
                resolve({
                    status: true,
                    message: '获取数据成功',
                    data: JSON.parse(result)
                });
            } catch (e) {
                resolve({
                    status: true,
                    message: '获取数据成功',
                    data: result
                });
            }
        });
    }
    setItem(key, value) {
        return new Promise((resolve, reject) => {
            try {
                localStorage.setItem(key, value);
                resolve({
                    status: true,
                    message: '新增数据成功',
                    data: null
                });
            } catch (e) {
                reject(e);
            }
        });
    }
    removeItem(key) {
        return new Promise((resolve, reject) => {
            try {
                localStorage.removeItem(key);
                resolve({
                    status: true,
                    message: '删除数据成功',
                    data: null
                });
            } catch (e) {
                reject(e);
            }
        });
    }
    clear() {
        return new Promise((resolve, reject) => {
            try {
                localStorage.clear();
                resolve({
                    status: true,
                    message: '清除所有数据成功',
                    data: null
                });
            } catch (e) {
                reject(e);
            }
        })
    }
    getAll() {
        return new Promise((resolve, reject) => {
            try {
                const len = localStorage.length;
                const items = [];
                for (let i = 0; i < len; i++) {
                    const key = localStorage.key(i);
                    const value = localStorage.getItem(key);
                    items.push({key: value});
                }
                resolve({
                    status: true,
                    message: '获取所有数据成功',
                    data: items
                })
            } catch (e) {
                reject(e);
            }
        })
    }
}

实现IndexDB类

// 这个方法用于打开 indexDB 数据库并返回 Promise 对象。
function openDatabase(databaseName, version, storeName = 'store_personal') {
    return new Promise((resolve, reject) => {
        const request = indexedDB.open(databaseName, version);
        request.onupgradeneeded = (event) => {
            const db = event.target.result;
            // 为数据库创建对象存储(objectStore)
            const objectStore = db.createObjectStore(storeName, {
                keyPath: "key",
                autoIncrement: true
            });
            // 创建一个索引
            objectStore.createIndex("key", "key", {unique: false});
        };
        request.onsuccess = (event) => {
            resolve(event);
        };
        request.onerror = (event) => {
            console.error(`数据库错误:${event.target.errorCode}`);
            reject(event.target.errorCode);
        };
    });
}
class IndexDB {
    constructor(databaseName,version,storeName) {
        this.IDBDatabase = openDatabase(databaseName,version,storeName);
        this.storeName = storeName;
    }
    getItem(key) {
        return new Promise(async (resolve) => {
            const IDBDatabase = await this.IDBDatabase; // 等待外界拿到indexDB数据库实例。
            const transaction = IDBDatabase.target.result.transaction([this.storeName], 'readonly');
            const objectStore = transaction.objectStore(this.storeName);
            const request = objectStore.get(key);
            request.onsuccess = (event) => {
                resolve({
                    status: true,
                    message: '查找成功',
                    data: event.target.result || request.result
                });
            };
            request.onerror = (event) => {
                resolve({
                    status: false,
                    message: '查找失败',
                    data: event.target.error || null
                });
            }
        });
    }
    setItem(key, value) {
        return new Promise(async (resolve, reject) => {
            const IDBDatabase = await this.IDBDatabase;
            const transaction = IDBDatabase.target.result.transaction([this.storeName], 'readwrite');
            const objectStore = transaction.objectStore(this.storeName);
            // put方法如果之前存在则修改,否则新增。如果想要put修改生效则需要设置主键让数据库知道哪些数据是相同的。
            const request = objectStore.put({key, value});
            request.onsuccess = () => {
                resolve({
                    status: true,
                    message: '新增成功',
                    data: {key, value}
                })
            }
            request.onerror = (event) => {
                resolve({
                    status: false,
                    message: '新增失败',
                    data: event.target.error || null
                });
            }
        });
    }
    deleteItem(key) {
        return new Promise(async (resolve) => {
            const IDBDatabase = await this.IDBDatabase;
            const transaction = IDBDatabase.target.result.transaction([this.storeName], 'readwrite');
            const objectStore = transaction.objectStore(this.storeName);
            const request = objectStore.delete(key);
            request.onsuccess = (event) => {
                resolve({
                    status: true,
                    message: '删除成功',
                    data: event.target.result || request.result
                });
            }
            request.onerror = (event) => {
                resolve({
                    status: false,
                    message: '删除失败',
                    data: event.target.error || null
                });
            }
        });
    }
    clear() {
        return new Promise(async (resolve) => {
            const IDBDatabase = await this.IDBDatabase;
            const transaction = IDBDatabase.target.result.transaction([this.storeName], 'readwrite');
            const objectStore = transaction.objectStore(this.storeName);
            const request = objectStore.clear();
            request.onsuccess = (event) => {
                resolve({
                    status: true,
                    message: '数据清除成功',
                    data: null
                });
            }
            request.onerror = (event) => {
                resolve({
                    status: false,
                    message: '数据清除失败',
                    data: event.target.error || null
                });
            }
        });
    }
    getAll() {
        return new Promise(async (resolve) => {
            const IDBDatabase = await this.IDBDatabase;
            const transaction = IDBDatabase.target.result.transaction([this.storeName], 'readwrite');
            const objectStore = transaction.objectStore(this.storeName);
            const request = objectStore.getAll();
            request.onsuccess = (event) => {
                resolve({
                    status: true,
                    message: '获取所有数据成功',
                    data: event.target.result || request.result
                });
            }
            request.onerror = (event) => {
                resolve({
                    status: false,
                    message: '获取所有数据失败',
                    data: event.target.error || null
                });
            }
        });
    }
}

根据浏览器支持情况选择对应的类:

function useStore() {
    const _indexDB = new IndexDB('store', 1, 'store_personal');
    const _localStorage = new LocalStorage();
    const strategy = window.indexedDB !== undefined ? _indexDB : _localStorage;
    return {
        getItem(key) {
            return strategy.getItem(key);
        },
        setItem(key,value) {
            return strategy.setItem(key,value);
        },
        removeItem(key) {
            return strategy.removeItem(key);
        },
        clear(){
            return strategy.clear();
        },
        getAll(){
            return strategy.getAll();
        }
    }
}