浏览器本地离线数据库IndexedDB封装

let _IDBDatabase;//数据库对象

/**
 * 初始化或升级数据库(动态对比增删改上一版本对象仓库和索引)
 * 
 * @param {String} dbName 数据库名称
 * @param {Number} dbVersion 数据库版本
 * @param {Array} objectStoreArr 对象仓库名称列表,格式如下:[{ name: 'userStore', index: [{ keys: 'name,age', unique: true }]}]
 * 
 * @returns Promise对象,then(IDBDatabase)
 */
export let initDB = (dbName, dbVersion, objectStoreArr) => new Promise((resolve, reject) => {
    let _IDBRequest = window.indexedDB.open(dbName, dbVersion);
    _IDBRequest.onsuccess = event => {
        _IDBDatabase = event.target.result;
        resolve(_IDBDatabase);
    };
    _IDBRequest.onerror = event => reject(event);
    _IDBRequest.onupgradeneeded = event => {
        let oldObjectStoreNames = event.target.result.objectStoreNames;
        let newObjectStoreNames = objectStoreArr.map(o => o.name);

        if (oldObjectStoreNames.length === 0 && newObjectStoreNames && newObjectStoreNames.length > 0) //原对象仓库为空,新对象仓库不为空,直接添加新对象仓库
            for (let newObjectStoreName of newObjectStoreNames) {
                let newObjectStore = event.target.result.createObjectStore(newObjectStoreName, { keyPath: 'id' });
                let newIndexArr = objectStoreArr.filter(o => o.name === newObjectStoreName)[0].index ?? [];
                for (let index of newIndexArr)
                    newObjectStore.createIndex(index.keys, index.keys.split(','), { unique: index.unique ?? false });
            }

        else {//原对象仓库不为空,则需要根据新旧对象仓库对比情况,决定添加删除或编辑

            for (let oldObjectStoreName of oldObjectStoreNames)
                if (!newObjectStoreNames.some(newObjectStoreName => newObjectStoreName === oldObjectStoreName))//新对象仓库中不存在的就对象仓库,直接删除
                    event.target.result.deleteObjectStore(oldObjectStoreName);

            newObjectStoreNames.forEach(newObjectStoreName => {
                if (oldObjectStoreNames.contains(newObjectStoreName)) {//新旧对象仓库都存在的,编辑
                    let oldObjectStore = event.target.transaction.objectStore(newObjectStoreName);

                    let newIndexArr = objectStoreArr.filter(o => o.name === newObjectStoreName)[0].index ?? [];

                    let oldIndexNames = oldObjectStore.indexNames;
                    let newIndexNames = newIndexArr.map(o => o.keys);

                    for (let oldIndexName of oldIndexNames)//旧对象仓库中存在索引,新对象仓库中不存在的索引,删除
                        if (!newIndexNames.some(newIndexName => newIndexName === oldIndexName))
                            oldObjectStore.deleteIndex(oldIndexName);

                    for (let newIndexName of newIndexNames) //新对象仓库中存在索引,旧对象仓库中不存在索引,添加
                        if (!oldIndexNames.contains(newIndexName))
                            oldObjectStore.createIndex(newIndexName, newIndexName.split(','), { unique: newIndexArr.filter(o => o.keys === newIndexName)[0].unique ?? false });

                } else {//新对象仓库有,旧对象仓库没有,添加
                    let newObjectStore = event.target.result.createObjectStore(newObjectStoreName, { keyPath: 'id' });
                    let newObjectStoreIndexs = objectStoreArr.filter(o => o.name === newObjectStoreName)[0].index ?? [];
                    for (let index of newObjectStoreIndexs)
                        newObjectStore.createIndex(index.keys, index.keys.split(','), { unique: index.unique ?? false });
                }
            })
        }
    };
});

1.初始化或升级IDB,调用如下

//调用示例
initDB("testDB", 1, [{ name: 'userStore', index: [{ keys: 'name,age', unique: true }, { keys: 'age', unique: false }] }, { name: 'scoreStore' }]).then(res => { });

2.添加数据

/**
 * 添加数据
 * 
 * @param {String} storeName 对象仓库名称
 * @param {Object} data 对象数据
 * 
 * @returns Promise对象,then(IDBRequest)
 */
export let adData = (storeName, data) => new Promise((resolve, reject) => {
    let _IDBRequest = _IDBDatabase.transaction(storeName, 'readwrite').objectStore(storeName).add(data);
    _IDBRequest.onsuccess = event => resolve(event.target);
    _IDBRequest.onerror = event => reject(event);
});
//调用示例
for (let i = 1; i <= 100; i++) {
  adData('userStore', { id: i, name: '张三' + i, age: Math.floor((Math.random() * 10) + 10) })
}

3.删除数据

/**
 * 删除数据
 * 
 * @param {String} storeName 对象仓库名称
 * @param {Number} id 对象数据id
 * 
 * @returns Promise对象,then(IDBRequest)
 */
export let rmDataById = (storeName, id) => new Promise((resolve, reject) => {
    let _IDBRequest = _IDBDatabase.transaction(storeName, 'readwrite').objectStore(storeName).delete(id);
    _IDBRequest.onsuccess = event => resolve(event.target);
    _IDBRequest.onerror = event => reject(event);
});
//调用示例
rmDataById('userStore', 3).then(res => console.log(res));

4.清空对象数据

/**
 * 清空对象数据
 * 
 * @param {String} storeName 对象仓库名称
 * 
 * @returns Promise对象,then(IDBRequest)
 */
export let clearData = (storeName) => new Promise((resolve, reject) => {
    let _IDBRequest = _IDBDatabase.transaction(storeName, 'readwrite').objectStore(storeName).clear();
    _IDBRequest.onsuccess = event => resolve(event.target);
    _IDBRequest.onerror = event => reject(event);
});
//调用示例
clearData('userStore').then(res => console.log(res));

5.对象仓库记录数据

/**
 * 对象仓库记录数
 * 
 * @param {String} storeName 对象仓库名称
 * @param {IDBKeyRange} query IDBKeyRange(可选)
 * @param {String} indexKeys 索引keys(缺省为id,可选)
 * 
 * @returns Promise对象,then(数据仓库记录数)
 */
export let countData = (storeName, query, indexKeys) => new Promise((resolve, reject) => {
    let objectStore = _IDBDatabase.transaction(storeName, 'readwrite').objectStore(storeName);
    if (indexKeys)
        objectStore = objectStore.index(indexKeys);
    let _IDBRequest = query ? objectStore.count(query) : objectStore.count();
    _IDBRequest.onsuccess = event => resolve(event.target.result);
    _IDBRequest.onerror = event => reject(event);
});
//调用示例
countData('userStore', IDBKeyRange.bound([10], [12]), 'age').then(res => console.log(res));

6.编辑数据

/**
 * 编辑数据
 * 
 * @param {String} storeName 对象仓库名称
 * @param {Object} data 对象数据
 * @returns Promise对象,then(IDBRequest)
 */
export let mdDataById = (storeName, data) => new Promise((resolve, reject) => {
    let _IDBRequest = _IDBDatabase.transaction(storeName, 'readwrite').objectStore(storeName).put(data);
    _IDBRequest.onsuccess = event => resolve(event.target);
    _IDBRequest.onerror = event => reject(event);
});
//调用示例
mdDataById('userStore', { id: 2, name: '李四222', age: 18 }).then(res => console.log(res))

7.根据id查询数据

/**
 * 根据id查询数据
 * 
 * @param {String} storeName 对象仓库名称
 * @param {Number} id    主键key 
 * 
 * @returns Promise对象,then(数据对象)
 */
export let getDataById = (storeName, id) => new Promise((resolve, reject) => {
    let _IDBRequest = _IDBDatabase.transaction(storeName, 'readwrite').objectStore(storeName).get(id);
    _IDBRequest.onsuccess = event => resolve(event.target.result);
    _IDBRequest.onerror = event => reject(event);
});
//调用示例
getDataById('userStore', 5).then(res => console.log(res))

8.查询数据列表

/**
 * 查询数据列表(自定义索引的query参数为数组格式,IDBKeyRange.bound([10], [20]))
 * 
 * @param {String} storeName 对象仓库名称
 * @param {IDBKeyRange} query IDBKeyRange(可选)
 * @param {IDBCursorDirection} direction IDBCursorDirection枚举类型:"next","nextunique","prev","prevunique"(可选)
 * @param {String} indexKeys 索引keys(缺省为id,可选)
 * 
 * @returns Promise对象,then(数据对象数组)
 */
export let queryData = (storeName, query, direction, indexKeys) => new Promise((resolve, reject) => {
    let objectStore = _IDBDatabase.transaction(storeName).objectStore(storeName);
    if (indexKeys)
        objectStore = objectStore.index(indexKeys);
    let _IDBRequest = query ? (direction ? objectStore.openCursor(query, direction) : objectStore.openCursor(query)) : objectStore.openCursor();
    let arr = [];
    _IDBRequest.onsuccess = event => {
        let cursor = event.target.result
        if (cursor) {
            arr.push(cursor.value);
            cursor.continue();
        } else
            resolve(arr);
    };
    _IDBRequest.onerror = event => reject(event);
});
//调用示例
queryData('userStore', IDBKeyRange.bound(1, 11, false, true)).then(res => console.log(res));

9.分页查询

/**
 * 分页查询
 * 
 * @param {String} storeName 对象仓库名称
 * @param {Number} pageNo 页码
 * @param {Number} pageSize 页面大小
 * @param {IDBKeyRange} query IDBKeyRange(可选)
 * @param {IDBCursorDirection} direction IDBCursorDirection枚举类型:"next","nextunique","prev","prevunique"(可选)
 * @param {String} indexKeys 索引keys(缺省为id,可选)
 
 * @returns Promise对象,then(分页数据 total、pages、data)
 */
export let queryPage = (storeName, pageNo, pageSize, query, direction, indexKeys) => countData(storeName, query, indexKeys).then(total => new Promise((resolve, reject) => {
    let pages = 0, data = [], objectStore = _IDBDatabase.transaction(storeName).objectStore(storeName);
    if (indexKeys)
        objectStore = objectStore.index(indexKeys);
    let _IDBRequest = query ? (direction ? objectStore.openCursor(query, direction) : objectStore.openCursor(query)) : objectStore.openCursor();
    let isAdvance = true;
    _IDBRequest.onsuccess = event => {
        let cursor = event.target.result
        if (cursor) {
            if (isAdvance && data.length === 0 && pageNo > 1) {
                isAdvance = false;
                cursor.advance((pageNo - 1) * pageSize);
            } else {
                data.push(cursor.value);

                if (data.length === pageSize) {
                    resolve({
                        total: total,
                        pages: Math.ceil(total / pageSize),
                        data: data
                    })
                    return;
                }

                cursor.continue();
            }
        } else
            resolve({
                total: total,
                pages: pages,
                data: data
            })
    };
    _IDBRequest.onerror = event => reject(event);
}));
//调用示例
queryPage('userStore', 1, 9).then(res => console.log(res))

10.关闭数据库

/**
 * 关闭数据库
 * 
 * @returns void
 */
export let closeDB = () => _IDBDatabase.close();
//调用示例
closeDB()
发表评论
留言与评论(共有 0 条评论) “”
   
验证码:

相关文章

推荐文章