indexedDB | 青训营笔记

107 阅读6分钟

这是我参与「第四届青训营」笔记创作活动的第5天。在实现js异常日志上报流程,运用到了indexedDB来暂存已上报的异常日志,以此来筛选重复发生的js异常。

indexedDB的基本概念:

  • indexedDB就是浏览器提供的本地数据库,允许我们储存大量数据(一般来说不少于 250MB),提供各种API,功能强大。
  • 数据库
  • 对象仓库:每个数据库包含若干个对象仓库。类似于关系型数据库的表格。
  • 数据记录:对象仓库保存的是数据记录,即数据库表中的一条条数据,只有主键和数据体两部分。
  • 以上三者之间的关系:数据库 > 对象仓库 > 数据记录

indexedDB的基本用法:

打开数据库

语法:indexedDB.open(databaseName, version)

var request = window.indexedDB.open("myDB", 1);//获取事务对象,异步操作
var db;

//新建或更新数据库时,则会调用该函数
request.onupgradeneeded = function(e) {
    db = e.target.result;
    console.log('upgradeneeded');
}

//如果要打开的数据库存在,则会直接打开该数据库
request.onsuccess = function(e) {
    //获取数据库对象
    db = request.result;
    console.log("success");
}

//数据库打开错误时调用此回调函数
request.onerror = function(e) {
    console.log("error");
}

新建或更新数据库

语法:request.onupgradeneeded(event) 在该方法中,我们会进行新建对象仓库(数据表)和新建索引。

  • IDBDatabase.createObjectStore()
    • 参数:
      • name(对象仓库名称)
      • options(配置对象)(可选的)
        • keyPath:将keyPath对应的值作为该对象仓库的主键
        • autoIncrement:由IndexedDB 自动生成主键
  • IDBObjectStore.createIndex()
    • 参数:
      • indexName(索引名称)
      • keyPath(索引对应的键名)
      • objectParameters(配置对象)(可选的)
        • unique:如果设为 true,将不允许重复的值
        • multiEntry:如果设为 true,对于有多个值的主键数组,每个值将在索引里面新建一个条目,否则主键数组对应一个条目。
request.onupgradeneeded = function(e) {
    db = e.target.result;
    // 新建对象仓库(即新建表)
    if (!db.objectStoreNames.contains('person')) {
        objectStore = db.createObjectStore('person', { keyPath: 'id' })
        // objectStore = db.createObjectStore('person', { autoIncrement: true })
        objectStore.createIndex('name', 'name', { unique: false })
        objectStore.createIndex('address', 'address', { unique: false })
        objectStore.createIndex('idCard', 'idCard', { unique: true })
    }
}

新增数据

在IndexedDB中,我们也能够使用事务来进行数据库的操作。事务有三个模式:

  • readOnly,只读。
  • readwrite,读写。
  • versionchange,数据库版本变化。 我们创建一个事务时,需要从上面选择一种模式,如果不指定的话,则默认为只读模式。
//db:数据库实例
//storename:仓库名称
//data:数据
addData (db, storename, data) {
    var request = db.transaction(storename, 'readwrite') // 事务对象 指定表格名称和操作模式("只读"或"读写")
    .objectStore(storename) // 获取仓库对象
    .add(data) //新增数据

    request.onsuccess = function (event) {
        console.log('数据写入成功')
    }

    request.onerror = function (event) {
        console.log('数据写入失败')
    }
}

查询数据

通过主键查询数据

objectStore.get()方法用于读取数据,参数是主键的值

//storename:仓库名称
//key:主键值
getDataByKey: function (db, storename, key) {
    var transaction = db.transaction([storename]) // 事务
    var objectStore = transaction.objectStore(storename) // 仓库对象
    var request = objectStore.get(key)

    request.onerror = function (event) {
        console.log('事务失败')
    }

    request.onsuccess = function (event) {
        console.log('主键查询结果: ', request.result)
    }
}

通过游标查询数据

使用指针对象 IDBCursor 遍历数据表格的所有记录

//db 数据库实例
//storename 仓库名称
cursorGetData: function (db, storename) {
    let list = []//注意list必须放在request.onsuccess函数外面,因为request.onsuccess函数会执行多次
    var store = db.transaction(storename, 'readwrite') // 事务
    .objectStore(storename) // 仓库对象
    //用于新建指针对象的openCursor()方法是一个异步操作
    var request = store.openCursor() // 指针对象
    request.onsuccess = function (e) {
        var cursor = e.target.result
        if (cursor) { // 必须要检查
            list.push(cursor.value)
            cursor.continue()// 遍历了存储对象中的所有内容
        } else {
            console.log('游标查询结果:', list)
        }
    }
}

通过索引查询数据

//db 数据库实例
//storename 仓库名称
//被查询索引名称
//被查询索引值
getDataByIndex: function (db, storename, indexName, indexValue) {
    var store = db.transaction(storename, 'readwrite') // 事务
    .objectStore(storename) // 仓库对象
    var request = store.index(indexName) // 索引对象
    .get(indexValue)
    // var request = store.index(indexName).getAll(indexValue);//返回所有的对应该索引的对象
    request.onerror = function () {
        console.log('事务失败')
    }
    request.onsuccess = function (e) {
        var result = e.target.result
        console.log('索引查询结果:', result)
    }
}

通过索引游标读取数据

与索引查询的区别:

  • 索引查询:查询满足条件的第一条数据
  • 索引和游标查询:满足条件的所有数据
//db:数据库实例
//storename:仓库名称
//indexName:索引名
//indexValue:索引值
cursorGetDataByIndex: function (db, storename, indexName, indexValue) {
    let list = []
    var store = db.transaction(storename, 'readwrite').objectStore(storename) // 仓库对象
    var request = store.index(indexName) // 索引对象
    .openCursor(IDBKeyRange.only(indexValue)) // 指针对象
    request.onsuccess = function (e) {
        var cursor = e.target.result
        if (cursor) { // 必须要检查
            list.push(cursor.value)
            cursor.continue()// 遍历了存储对象中的所有内容
        } else {
            console.log('游标索引查询结果:', list)
        }
    }
    request.onerror = function (e) {
    }
}

更新数据

put()方法会自动根据传递进去的对象中的主键来更新对应的数据记录

//db:数据库实例
//storename:表名称
//data:数据
updateData: function (db, storename, data) {
    var request = db.transaction([storename], 'readwrite') // 事务对象
    .objectStore(storename) // 仓库对象
    .put(data)

    request.onsuccess = function () {
        console.log('数据更新成功')
    }

    request.onerror = function () {
        console.log('数据更新失败')
    }
}

删除数据

通过主键删除数据

//db:数据库实例
//storename:表名称
//id:主键值
deleteData: function (db, storename, id) {
    var request = db.transaction([storename], 'readwrite') // 事务
    .objectStore(storename) // 仓库对象
    .delete(id)

    request.onsuccess = function () {
        console.log('数据删除成功')
    }

    request.onerror = function () {
        console.log('数据删除失败')
    }
}

通过索引和游标删除数据

//db:数据库实例
//storename:表名称
//indexName:索引名
//indexValue:索引值
cursorDeleteData: function (db, storename, indexName, indexValue) {
    var store = db.transaction(storename, 'readwrite').objectStore(storename) // 仓库对象
    var request = store.index(indexName) // 索引对象
    .openCursor(IDBKeyRange.only(indexValue)) // 指针对象
    request.onsuccess = function (e) {
        var cursor = e.target.result
        var deleteRequest
        if (cursor) {
            deleteRequest = cursor.delete()// 请求删除当前项
            deleteRequest.onerror = function () {
                console.log('游标删除该记录失败')
            }
            deleteRequest.onsuccess = function () {
                console.log('游标删除该记录成功')
            }
            cursor.continue()
        }
    }
    request.onerror = function (e) {
    }
}

封装class

class MyDB{
    constructor(){
        this.db = null;
    }
    //打开数据库
    openDB(){
        return new Promise((resolve, reject) => {
            const request = indexedDB.open('myDB', 1)
            const self = this;
            request.onsuccess = function (event) {
                self.db = event.target.result // 数据库对象
                console.log('数据库打开成功')
                resolve(self.db);
            }

            request.onerror = function (event) {
                console.log('数据库打开报错')
                reject('数据库打开报错')
            }

            request.onupgradeneeded = function (event) { // 数据库创建或升级的时候会触发
                console.log('onupgradeneeded')
                const db = event.target.result // 数据库对象
                var objectStore
                if (!db.objectStoreNames.contains('person')) {
                    objectStore = db.createObjectStore('person', { keyPath: 'id' })
                    objectStore.createIndex('name', 'name', { unique: false })
                    objectStore.createIndex('address', 'address', { unique: false })
                    objectStore.createIndex('idCard', 'idCard', { unique: true })
                }
            }
        })
    }
    // 新增数据 
    // db:数据库实例 
    // storename:仓库名称 
    // data:数据
    addData(db, storename, data) {
        var request = db.transaction(storename, 'readwrite') // 事务对象 指定表格名称和操作模式("只读"或"读写")
        .objectStore(storename) // 仓库对象
        .add(data)

        request.onsuccess = function (event) {
            console.log('数据写入成功')
        }

        request.onerror = function (event) {
            console.log('数据写入失败')
            throw new Error(event.target.error)
        }
    }
    //通过索引读取数据
    queryDataByIndex(storename, indexName, indexValue){
        var objectStore = this.db.transaction(storename,"readonly").objectStore(storename);
        var index = objectStore.index(indexName);
        var request = index.get(indexValue); //只返回第一个

        request.onsuccess = function(e){
            var result = e.target.result;
            if(result){
                console.log("result:",result);
                console.log("索引查询成功");
            }else{
                console.log("索引查询失败");
            }
        }
        request.onerror = function () {
            console.log('事务失败')
        }
    }
    //通过主键读取数据
    queryDataByKey(storename, key) {
        // return new Promise((resolve, reject) => {
        var transaction = this.db.transaction([storename]) // 事务
        var objectStore = transaction.objectStore(storename) // 仓库对象
        var request = objectStore.get(key)

        request.onsuccess = function (event) {
            console.log('主键查询结果: ', request.result)
            // resolve(request.result)
        }
        request.onerror = function (event) {
            console.log('事务失败')
        }
        // })
    }
    //通过游标读取数据
    queryDataByCursor(storename) {
        let list = []
        var store = this.db.transaction(storename, 'readwrite') // 事务
        .objectStore(storename) // 仓库对象
        var request = store.openCursor() // 指针对象
        request.onsuccess = function (e) {
            var cursor = e.target.result
            if (cursor) { // 必须要检查
                list.push(cursor.value);
                cursor.continue()// 遍历了存储对象中的所有内容
            } else {
                console.log('游标查询结果:', list)
            }
        }
        request.onerror = function (event) {
            console.log('事务失败')
        }
    }
    //通过索引游标读取数据
    queryDataByCursorIndex(storename, indexName, indexValue) {
        let list = []
        var store = this.db.transaction(storename, 'readwrite').objectStore(storename) // 仓库对象
        var request = store.index(indexName) // 索引对象
        .openCursor(IDBKeyRange.only(indexValue)) // 指针对象
        request.onsuccess = function (e) {
            var cursor = e.target.result
            if (cursor) { // 必须要检查
                list.push(cursor.value)
                cursor.continue()// 遍历了存储对象中的所有内容
            } else {
                console.log('游标索引查询结果:', list)
            }
        }
        request.onerror = function (e) {
            console.log('事务失败')
        } 
    }
}
const myDB = new MyDB();
myDB.openDB().then(db => {
    myDB.addData(db, 'person', {id: 1, name: 'zhangsan', address: '广东省', idCard: 123});
    myDB.queryDataByIndex('person', 'name', 'zhangsan');
    myDB.queryDataByKey('person', 1);
    myDB.queryDataByCursor('person');
    myDB.queryDataByCursorIndex('person', 'address', '广东省');
})

参考: