这是我参与「第四届青训营」笔记创作活动的第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', '广东省');
})
参考: