使用indexedDB的正确打开方式和多种使用场景
针对以下场景,在不使用库的情况下,如何使用indexedDB数据库, 下面将介绍使用直接使用indexedDB的痛点, 最后给出解决方案
痛点1: 一个页面一个数据库,一张数据表
一张数据表,大家应该都能够操作,但你会发现,只是简简单单的使用api,会出现一些意外情况,怎么解决这样的意外情况呢?请看下面的小demo
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div>id: <input id="id" /></div>
<br />
<div>str: <input id="str" /></div>
<br />
<button id="add">新增数据</button>
<button id="search">查询数据</button>
<button id="delete">删除数据</button>
<button id="remove">删除表</button>
<script>
const dataSheetName = "tableTest"
const dataBaseName = "dataABC"
let request = window.indexedDB.open(dataBaseName)
let dataBase = null;
let version = null;
request.onerror = (event) => {
console.error('dataBase打开报错', event.target.error)
}
request.onsuccess = (event) => {
dataBase = event.target.result
version = event.target.result.version
console.info('dataBase打开成功', event.target.result)
}
// 新建数据表
request.onupgradeneeded = (event) => {
dataBase = event.target.result
let objectStore = null
console.info('dataBase建表')
objectStore = dataBase.createObjectStore(dataSheetName, {
keyPath: "id"
})
// 新建索引
objectStore.createIndex("str", "str", {unique: false})
}
document.querySelector("#add").onclick = () => {
const id = document.querySelector("#id").value
const str = document.querySelector("#str").value
request = dataBase.transaction(dataSheetName, 'readwrite')
.objectStore(dataSheetName)
.put({id, str})
request.onsuccess = () => {
console.info('write data success')
}
request.onerror = (e) => {
console.error('write data fail')
}
}
document.querySelector("#search").onclick = () => {
const key = document.querySelector("#id").value
const objectStore = dataBase.transaction([dataSheetName]).objectStore(dataSheetName)
const request = objectStore.get(key)
request.onerror = (e) => {
console.error('read dataBase fail')
}
request.onsuccess = () => {
if (request.result) {
console.log('search successful!!!!', request.result)
} else {
console.info('not find any data')
}
}
}
document.querySelector("#delete").onclick = () => {
const key = document.querySelector("#id").value
request = dataBase.transaction([dataSheetName], 'readwrite')
.objectStore(dataSheetName)
.delete(key)
request.onsuccess = (e) => {
console.info('dataBase数据删除成功', e)
}
request.onerror = (e) => {
console.error('dataBase数据删除事务失败')
}
}
document.querySelector("#remove").onclick = () => {
dataBase.close();
version
console.log(version)
request = window.indexedDB.open(dataBaseName, version);
request.onerror = (event) => {
console.error(event.target.error)
};
request.onsuccess = (event) => {
dataBase.close();
version = event.target.result.version
console.log('delet success')
}
request.onupgradeneeded = (event) => {
dataBase = event.target.result;
version = dataBase.version;
dataBase.deleteObjectStore(dataSheetName);
console.log(event.target, 'delete success')
};
}
</script>
</body>
</html>
但是当你删除这张表, 刷新这个页面, 再次打开时,你就会发现不能正常进行操作了
Uncaught DOMException: Failed to execute ‘transaction’ on ‘IDBDatabase’: One of the specified object stores was not found.
why?
这是简单的操作,而且也经常用到,原因就是version
每次open一个数据库,如果需要操作数据表,就必须要在onupgradeneeded回调里面执行,但是只有version发生变化,才会再次调用onupgradeneeded
上面的原因就是删除表之后,改变了原来的version
下次再次打开, 就必须要重新建表,而建表的操作是需要onupgradeneeded,而onupgradeneeded回调执行,需要version再次发生变化
也就是初次建表,version为1,删除表, version为2
当再次建立表. 需要version为3,才会执行onupgradeneeded回调,重新见表
但是这个version一个变量不可以保存到在页面上,页面上的变量会随着页面刷新而被垃圾回收,
如何保存这个version下次还能再次使用且不影响下次页面重新建表
如何解决下面会讲到,先讲完所有案例会出现的意外情况先
痛点2:一个页面一个数据库,两张数据表
当存在同一个数据库,两个表时, 同时要初始化两个表
当删除表之后,下次再次打开,进行数据库增删改查操作,出错, 版本不对
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="tableA">
<p>tableA</p>
<div>id: <input class="id" /></div>
<br />
<div>str: <input class="str" /></div>
<br />
<button class="add">新增数据</button>
<button class="search">查询数据</button>
<button class="delete">删除数据</button>
<button class="remove">删除表</button>
</div>
<div id="tableB">
<p>tableB</p>
<div>id: <input class="id" /></div>
<br />
<div>str: <input class="str" /></div>
<br />
<button class="add">新增数据</button>
<button class="search">查询数据</button>
<button class="delete">删除数据</button>
<button class="remove">删除表</button>
</div>
<script>
let dataBase = null;
let version = null;
function openBase(dataBaseName, dataSheetName1, dataSheetName2) {
if(dataBase != null) {
dataBase.close()
}
let request = window.indexedDB.open(dataBaseName)
request.onerror = (event) => {
console.error('dataBase打开报错', event.target.error)
}
request.onsuccess = (event) => {
dataBase = event.target.result
version = event.target.result.version
console.info('dataBase打开成功', event.target.result, version)
}
// 新建数据表
request.onupgradeneeded = (event) => {
dataBase = event.target.result
// let objectStore = null
console.info('dataBase建表')
let objectStore1 = dataBase.createObjectStore(dataSheetName1, {
keyPath: "id"
})
// 新建索引
objectStore1.createIndex("str", "str", {unique: false})
let objectStore2 = dataBase.createObjectStore(dataSheetName2, {
keyPath: "id"
})
// 新建索引
objectStore2.createIndex("str", "str", {unique: false})
}
}
openBase("baseA", "tableA", "tableB")
document.querySelector("#tableA .add").onclick = () => {
const id = document.querySelector("#tableA .id").value
const str = document.querySelector("#tableA .str").value
request = dataBase.transaction("tableA", 'readwrite')
.objectStore("tableA")
.put({id, str})
request.onsuccess = () => {
console.info('tableA write data success')
}
request.onerror = (e) => {
console.error('tableA write data fail')
}
}
document.querySelector("#tableB .add").onclick = () => {
const id = document.querySelector("#tableB .id").value
const str = document.querySelector("#tableB .str").value
request = dataBase.transaction("tableB", 'readwrite')
.objectStore("tableB")
.put({id, str})
request.onsuccess = () => {
console.info('tableB write data success')
}
request.onerror = (e) => {
console.error('tableB write data fail')
}
}
document.querySelector("#tableA .search").onclick = () => {
const key = document.querySelector("#tableA .id").value
const objectStore = dataBase.transaction(['tableA']).objectStore('tableA')
const request = objectStore.get(key)
request.onerror = (e) => {
console.error('#tableA . read dataBase fail')
}
request.onsuccess = () => {
if (request.result) {
console.log('#tableA . search successful!!!!', request.result)
} else {
console.info('tableA not find any data')
}
}
}
document.querySelector("#tableB .search").onclick = () => {
const key = document.querySelector("#tableB .id").value
const objectStore = dataBase.transaction(['tableB']).objectStore('tableB')
const request = objectStore.get(key)
request.onerror = (e) => {
console.error('#tableB . read dataBase fail')
}
request.onsuccess = () => {
if (request.result) {
console.log('#tableB . search successful!!!!', request.result)
} else {
console.info('tableB not find any data')
}
}
}
document.querySelector("#tableA .delete").onclick = () => {
const key = document.querySelector("#tableA .id").value
request = dataBase.transaction(['tableA'], 'readwrite')
.objectStore('tableA')
.delete(key)
request.onsuccess = (e) => {
console.info('tableA dataBase数据删除成功', e)
}
request.onerror = (e) => {
console.error('tableA dataBase数据删除事务失败')
}
}
document.querySelector("#tableB .delete").onclick = () => {
const key = document.querySelector("#tableB .id").value
request = dataBase.transaction(['tableB'], 'readwrite')
.objectStore('tableB')
.delete(key)
request.onsuccess = (e) => {
console.info('tableB dataBase数据删除成功', e)
}
request.onerror = (e) => {
console.error('tableB dataBase数据删除事务失败')
}
}
document.querySelector("#tableA .remove").onclick = () => {
dataBase.close();
version
console.log(version)
request = window.indexedDB.open('baseA', version);
request.onerror = (event) => {
console.error(event.target.error)
};
request.onsuccess = (event) => {
// dataBase.close();
version = event.target.result.version
console.log('delet success')
}
request.onupgradeneeded = (event) => {
dataBase = event.target.result;
version = dataBase.version;
dataBase.deleteObjectStore('tableA');
console.log(event.target, 'tableA delete success')
};
}
document.querySelector("#tableB .remove").onclick = () => {
dataBase.close();
version
console.log(version)
request = window.indexedDB.open('baseA', version);
request.onerror = (event) => {
console.error(event.target.error)
};
request.onsuccess = (event) => {
// dataBase.close();
version = event.target.result.version
console.log('delet success')
}
request.onupgradeneeded = (event) => {
dataBase = event.target.result;
version = dataBase.version;
dataBase.deleteObjectStore('tableB');
console.log(event.target, ' tableB delete success')
};
}
</script>
</body>
</html>
报错依然很眼熟,没有重新建表,就去使用这个表了, indexedDB中,表就是store
Uncaught DOMException: Failed to execute ‘transaction’ on ‘IDBDatabase’: One of the specified object stores was not found.
这个跟上面的错误原因是一样的
并且删除表的时候,也是报错version的原因
DOMException: Version change transaction was aborted in upgradeneeded event handler.
痛点3:一个页面多个数据库,多张表
当存在不同数据库, 不同表, 初始化不可以共用, 且数据库不同, 所以争对不同的数据库操作也是根据数据库来的
当删除表之后,下次再次打开,进行数据库增删改查操作,出错, 版本不对
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="tableA">
<p>baseA, tableA</p>
<div>id: <input class="id" /></div>
<br />
<div>str: <input class="str" /></div>
<br />
<button class="add">新增数据</button>
<button class="search">查询数据</button>
<button class="delete">删除数据</button>
<button class="remove">删除表</button>
</div>
<div id="tableB">
<p>baseB, tableB</p>
<div>id: <input class="id" /></div>
<br />
<div>str: <input class="str" /></div>
<br />
<button class="add">新增数据</button>
<button class="search">查询数据</button>
<button class="delete">删除数据</button>
<button class="remove">删除表</button>
</div>
<script>
let version = null;
let dataBaseA = null;
let dataBaseB = null;
function openBase1() {
let request = window.indexedDB.open("baseA")
request.onerror = (event) => {
console.error('dataBase打开报错', event.target.error)
}
request.onsuccess = (event) => {
dataBaseA = event.target.result
version = event.target.result.version
console.log('dataBaseA open success')
}
// 新建数据表
request.onupgradeneeded = (event) => {
dataBaseA = event.target.result
// let objectStore = null
console.info('datadataBaseA建表')
let objectStore1 = dataBaseA.createObjectStore("tableA", {
keyPath: "id"
})
// 新建索引
objectStore1.createIndex("str", "str", {unique: false})
}
}
function openBase2() {
let request = window.indexedDB.open("baseB")
request.onerror = (event) => {
console.error('dataBase打开报错', event.target.error)
}
request.onsuccess = (event) => {
dataBaseB = event.target.result
version = event.target.result.version
console.log('dataBaseB open success')
}
// 新建数据表
request.onupgradeneeded = (event) => {
dataBaseB = event.target.result
// let objectStore = null
console.info('dataBaseb建表')
let objectStore1 = dataBaseB.createObjectStore("tableB", {
keyPath: "id"
})
// 新建索引
objectStore1.createIndex("str", "str", {unique: false})
}
}
openBase1()
openBase2()
document.querySelector("#tableA .add").onclick = () => {
const id = document.querySelector("#tableA .id").value
const str = document.querySelector("#tableA .str").value
request = dataBaseA.transaction("tableA", 'readwrite')
.objectStore("tableA")
.put({id, str})
request.onsuccess = () => {
console.info('tableA write data success')
}
request.onerror = (e) => {
console.error('tableA write data fail')
}
}
document.querySelector("#tableB .add").onclick = () => {
const id = document.querySelector("#tableB .id").value
const str = document.querySelector("#tableB .str").value
console.log(dataBaseB, 11111)
request = dataBaseB.transaction("tableB", 'readwrite')
.objectStore("tableB")
.put({id, str})
request.onsuccess = () => {
console.info('tableB write data success')
}
request.onerror = (e) => {
console.error('tableB write data fail')
}
}
document.querySelector("#tableA .search").onclick = () => {
const key = document.querySelector("#tableA .id").value
const objectStore = dataBaseA.transaction(['tableA']).objectStore('tableA')
const request = objectStore.get(key)
request.onerror = (e) => {
console.error('#tableA . read dataBase fail')
}
request.onsuccess = () => {
if (request.result) {
console.log('#tableA . search successful!!!!', request.result)
} else {
console.info('tableA not find any data')
}
}
}
document.querySelector("#tableB .search").onclick = () => {
const key = document.querySelector("#tableB .id").value
const objectStore = dataBaseB.transaction(['tableB']).objectStore('tableB')
const request = objectStore.get(key)
request.onerror = (e) => {
console.error('#tableB . read dataBase fail')
}
request.onsuccess = () => {
if (request.result) {
console.log('#tableB . search successful!!!!', request.result)
} else {
console.info('tableB not find any data')
}
}
}
document.querySelector("#tableA .delete").onclick = () => {
const key = document.querySelector("#tableA .id").value
request = dataBaseA.transaction(['tableA'], 'readwrite')
.objectStore('tableA')
.delete(key)
request.onsuccess = (e) => {
console.info('tableA dataBase数据删除成功', e)
}
request.onerror = (e) => {
console.error('tableA dataBase数据删除事务失败')
}
}
document.querySelector("#tableB .delete").onclick = () => {
const key = document.querySelector("#tableB .id").value
request = dataBaseB.transaction(['tableB'], 'readwrite')
.objectStore('tableB')
.delete(key)
request.onsuccess = (e) => {
console.info('tableB dataBase数据删除成功', e)
}
request.onerror = (e) => {
console.error('tableB dataBase数据删除事务失败')
}
}
document.querySelector("#tableA .remove").onclick = () => {
dataBaseA.close();
version
console.log(version)
request = window.indexedDB.open('baseA', version);
request.onerror = (event) => {
console.error(event.target.error)
};
request.onsuccess = (event) => {
version = event.target.result.version
console.log('delet success')
}
request.onupgradeneeded = (event) => {
dataBaseA = event.target.result;
version = dataBaseA.version;
dataBaseA.deleteObjectStore('tableA');
console.log(event.target, 'tableA delete success')
};
}
document.querySelector("#tableB .remove").onclick = () => {
dataBaseB.close();
version
console.log(version)
request = window.indexedDB.open('baseB', version);
request.onerror = (event) => {
console.error(event.target.error)
};
request.onsuccess = (event) => {
version = event.target.result.version
console.log('delet success')
}
request.onupgradeneeded = (event) => {
dataBaseB = event.target.result;
version = dataBaseB.version;
dataBaseB.deleteObjectStore('tableB');
console.log(event.target, ' tableB delete success')
};
}
</script>
</body>
</html>
看到这里, 大家会不会觉得每次删除数据库之后, 操作就会出现问题,那不删除数据表, 是不是就没有问题了?
确实没有删除表, 数据就能下次继续这样操作, 那是不是就可以这样封装了呢?
等等, 还有一种情况
痛点4:初始化页面就立刻进行数据表操作
这是什么操作呢?
就是页面一初始化完成, 就立刻往数据表里面新增内容, 或者立刻查询数据表, 这个时候会出现什么情况呢?
请看下面例子:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div>id: <input id="id" /></div>
<br />
<div>str: <input id="str" /></div>
<br />
<button id="add">新增数据</button>
<button id="search">查询数据</button>
<button id="delete">删除数据</button>
<button id="remove">删除表</button>
<script>
const dataSheetName = "tableTest"
const dataBaseName = "dataABC"
let request = window.indexedDB.open(dataBaseName)
let dataBase = null;
let version = null;
request.onerror = (event) => {
console.error('dataBase打开报错', event.target.error)
}
request.onsuccess = (event) => {
dataBase = event.target.result
version = event.target.result.version
console.info('dataBase打开成功', event.target.result)
}
// 新建数据表
request.onupgradeneeded = (event) => {
dataBase = event.target.result
let objectStore = null
console.info('dataBase建表')
objectStore = dataBase.createObjectStore(dataSheetName, {
keyPath: "id"
})
// 新建索引
objectStore.createIndex("str", "str", {unique: false})
}
const initAdd = () => {
// const id = document.querySelector("#id").value
const id = "123"
const str = "1234"
// const str = document.querySelector("#str").value
request = dataBase.transaction(dataSheetName, 'readwrite')
.objectStore(dataSheetName)
.put({id, str})
console.log('add')
request.onsuccess = () => {
console.info('write data success')
}
request.onerror = (e) => {
console.error('write data fail')
}
}
initAdd();
</script>
</body>
</html>
Uncaught TypeError: Cannot read properties of null (reading ‘transaction’)
报错说dataBase为null
我们查看一下MDN文档里面是怎么说的
https://developer.mozilla.org/zh-CN/docs/Web/API/IndexedDB_API
简单的情况,看起来很复杂, 其实就是说想要直接使用, 步骤比较多, 遇到的问题也很多
我们继续看, 如何使用indexedDB
https://developer.mozilla.org/zh-CN/docs/Web/API/IndexedDB_API/Using_IndexedDB
IndexedDB 鼓励使用的基本模式如下所示:
- 打开数据库。
- 在数据库中创建一个对象仓库(object store)。
- 启动一个事务,并发送一个请求来执行一些数据库操作,像增加或提取数据等。
- 通过监听正确类型的 DOM 事件以等待操作完成。
- 在操作结果上进行一些操作(可以在 request 对象中找到)
这些基本模式, 我们通过上面的例子, 也已经非常清楚和了解了
这才是核心, 所有操作都是异步的, 这也很好解释了, 上面的报错, 就是因为dataBase还未初始化完毕, 所以导致的错误
只有当open的success回调执行完,dataBase才算真正初始化完成
解决: 封装一个简单的工具类
实现增删改查,实现上面这多种操作,复杂操作,例如阻塞未实现
首先我们考虑一下能否使用类来实现?一个数据表对应一个对象
异步回调,需要使用到promise,使用时可以用then继续回调,或者用async和await关键字
接下来,我们考虑初始化一张表需要执行的操作
然后就可以开始封装
我们初始化一张表格,需要哪些参数:
数据库名称(baseName)
数据表名称(sheetName)
主键(keyName)
索引(indexArr 数组)
version(版本)
直接看完整封装
export default class IndexedDB {
dataBase = null; // 数据库
dataBaseName = null; // 数据库名
dataSheetName = null; // 数据表名
keyName = '' // 主键
indexArr = [] // 索引
version = null
constructor({
baseName,
sheetName,
keyName,
indexArr,
version
}) {
// baseName: 数据库名称
// sheetName:数据表名称
// keyName:主键
// indexArr: Array<name, unique> 索引<索引名, 索引是否重复(为true则表示同一个项的值不能重复)>
this.dataBaseName = baseName
this.dataSheetName = sheetName
this.keyName = keyName
this.indexArr = indexArr
this.version = version
}
isReady(version) {
return new Promise((resolve, reject) => {
// 打开数据库
const request = window.indexedDB.open(this.dataBaseName, version || this.version)
request.onerror = (event) => {
console.error('dataBase打开报错', event.target.error)
reject(event.target.error)
}
request.onsuccess = (event) => {
this.dataBase = event.target.result
this.version = event.target.result.version
console.info('dataBase打开成功', event.target.result)
resolve()
}
// 新建数据表
request.onupgradeneeded = (event) => {
this.dataBase = event.target.result
let objectStore = null
if (!this.dataBase.objectStoreNames.contains(this.dataSheetName)) { // 不存在该数据表
console.info('dataBase建表')
objectStore = this.dataBase.createObjectStore(this.dataSheetName, {
keyPath: this.keyName
})
// 新建索引
this.indexArr.forEach(item => {
objectStore.createIndex(item.name, item.name, {
unique: item.unique || false
})
})
}
}
})
}
// 写数据操作
write(obj) {
return new Promise((resolve, reject) => {
// put()有则更新,无则新增
const request = this.dataBase.transaction(this.dataSheetName, 'readwrite')
.objectStore(this.dataSheetName)
.put(obj)
request.onsuccess = () => {
console.info('write data success')
resolve(this)
}
request.onerror = (e) => {
console.error('write data fail')
reject(e.target.error)
}
})
}
// 新增数据
add(obj) {
return new Promise((resolve, reject) => {
if (this.dataBase.objectStoreNames.contains(this.dataSheetName)) { // 确定有表
this.write(obj).then(() => {
resolve()
}).catch(err => {
console.error(err);
reject(err)
})
} else { // 如果表未成功建立
// 第一步断开库连接
this.dataBase.close()
// 新增版本(必须新增版本,否则不会执行onupgradeneeded, 而表操作必须在onupgradeneeded回调中执行)
this.version ;
// 第二步// 重新建表
this.isReady().then(() => {
// 重新写入数据
this.write(obj).then(() => {
resolve()
})
}).catch(err => {
console.error(err);
reject(err)
})
}
})
}
// 更新数据
update(obj) {
// 没有该索引就新增,有就更新
return new Promise((resolve, reject) => {
this.add(obj).then(() => {
resolve()
}).catch((e) => {
reject(e)
})
})
}
// 读取数据
read(key) {
// throw new Error('xxxxxxxxx')
return new Promise((resolve, reject) => {
// throw new Error('1111111')
if (!this.dataBase.objectStoreNames.contains(this.dataSheetName)) {
return resolve('未建表')
}
const objectStore = this.dataBase.transaction([this.dataSheetName]).objectStore(this.dataSheetName)
const request = objectStore.get(key)
request.onerror = (e) => {
console.error('read dataBase fail')
reject(e.target.error)
}
request.onsuccess = () => {
if (request.result) {
resolve(request.result)
} else {
console.info('not find any data')
resolve(null)
}
}
})
}
// 删除数据
remove(key) {
return new Promise((resolve, reject) => {
// 不管有没有这个索引,都是删除成功的回调
if (!this.dataBase.objectStoreNames.contains(this.dataSheetName)) {
return resolve("未建该表")
}
const request = this.dataBase.transaction([this.dataSheetName], 'readwrite')
.objectStore(this.dataSheetName)
.delete(key)
request.onsuccess = (e) => {
console.info('dataBase数据删除成功', e)
resolve()
}
request.onerror = (e) => {
console.error('dataBase数据删除事务失败')
reject(e.target.error)
}
})
}
// 删除数据库
deleDataBase(dataBaseName) {
return new Promise((resolve, reject) => {
const DBDeleteRequest = window.indexedDB.deleteDatabase(dataBaseName);
this.dataBase.close();
DBDeleteRequest.onerror = (event) => {
console.error("Error deleting database.");
reject(event.target.error)
};
DBDeleteRequest.onsuccess = (event) => {
console.log("Database deleted successfully", event);
console.log(event.result); // should be undefined
resolve(event.result)
};
})
}
// 删除表
deleSheet(version) {
return new Promise((resolve, reject) => {
const request = window.indexedDB.open(this.dataBaseName, version);
request.onerror = (event) => {
// Handle errors.
console.error(event.target.error)
reject(event.target.error)
};
request.onsuccess = (event) => {
this.dataBase.close();
console.log('delet success')
}
request.onupgradeneeded = (event) => {
this.dataBase = event.target.result;
this.version = this.dataBase.version;
this.dataBase.deleteObjectStore(this.dataSheetName);
// this.dataBase.close();
//删除之后不需要再关闭,否则会报错 DOMException: The connection was closed
console.log(event.target, 'delete success')
resolve(event)
};
})
}
destroy() {
this.dataBase.close();
console.log('数据库连接关闭')
}
}
1.为什么初始化时,不执行open数据库?
2.为什么isReady()可传参,可不传参,什么情况适合传参?
3.为什么新增和修改数据时,需要先判断是否有表?
4.为什么删除和查询,不需要再次更新版本
接下来通过demo来解释
操作一张表
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>操作一张表</title>
<style>
.list {
margin-top: 40px;
}
.box {
border: 1px solid #ccc;
padding: 50px;
}
</style>
</head>
<body>
<div id="list">
<div class="student box">
<p>student表</p>
<div>id: <input class="id" /></div>
<br />
<div>str: <input class="str" /></div>
<br />
<button class="add">新增数据</button>
<button class="search">查找数据</button>
<button class="romove">删除数据</button>
<button class="update">更新数据</button>
<button class="delete">删除表</button>
<br />
</div>
</div>
<br />
<button id="deleteBase">删除库</button>
<script type="module">
import IndexedDB from '../util/indexedDB.js'
// 不同数据库之间不需要open和关闭连接,也能够实现增删改查
const studentbaseTable = {
baseName: 'student', // 数据库名称
sheetName: 'studentTable', // 数据表名称
keyName: 'id', // 主键
indexArr: [{ name: 'str' }] // 索引
}
const student = new IndexedDB(studentbaseTable)
document.querySelector("#list .student .add").onclick = () => {
const id = document.querySelector("#list .student .id").value
const str = document.querySelector("#list .student .str").value
if(id !== "" && str !== "") {
student.isReady().then(() => {
return student.add({
id,
str
})
}).then(() => {
student.destroy()
}).catch(e=> {
console.error(e)
})
} else {
confirm("主键和str不能为空")
}
}
document.querySelector("#list .student .search").onclick = () => {
const id = document.querySelector("#list .student .id").value
if(id !== "") {
student.isReady().then(() => {
return student.read(id)
}).then(res => {
console.log('读取的数据为', res)
confirm('str: ' res.str)
}).then(() => {
student.destroy()
}).catch(e=> {
console.error(11, e)
}).finally(() => {
console.log(22222)
})
} else {
confirm("根据主键查找,主键不能为空")
}
}
document.querySelector("#list .student .romove").onclick = () => {
const id = document.querySelector("#list .student .id").value
if(id !== "") {
student.isReady().then(() => {
return student.remove(id)
}).then(res => {
confirm('删除成功')
student.destroy()
}).catch(e => {
confirm('删除失败')
})
} else {
confirm("根据主键删除,主键不能为空")
}
}
document.querySelector("#list .student .update").onclick = () => {
const id = document.querySelector("#list .student .id").value
const str = document.querySelector("#list .student .str").value
if(id !== "" && str !== "") {
student.isReady().then(() => {
return student.update({
id,
str
})
}).then((res) => {
confirm('更新成功')
student.destroy()
})
} else {
confirm("主键和str不能为空")
}
}
document.querySelector("#list .student .delete").onclick = () => {
student.isReady().then(() => {
return student.destroy()
}).then(() => {
return student.deleSheet(student.version 1)
}).then(() => {
confirm('删除成功')
}).catch((e) => {
confirm('删除表失败')
console.error(e)
})
}
document.querySelector("#deleteBase").onclick = () => {
student.isReady().then(() => {
student.deleDataBase('student').then(() => {
confirm('删除库成功')
}).catch((e) => {
confirm('删除库失败')
console.error(e)
})
})
}
</script>
</body>
</html>
1.为什么初始化时,不执行open数据库?
通过这个demo,可以解释第一个疑问,没有执行open,是为了每次操作时就执行isReady()操作,也就是说只要进行数据表的增删改查再去新建这张表,这样就不存在,未建表时,进行表操作;如果这张表已经建好了,那自然而然不会执行onupgradeneeded回调
每次新增完这张数据表,就可以关闭数据库连接,只要不频繁的进行数据库的增删改查操作,我觉得就能关闭数据库连接,以免忘记关闭,同时也防止出现多次open数据库的情况
多次open数据库,但是没有及时close,会导致删除表操作时,卡死
删除表时,我们知道,需要版本号进行修改,且执行onupgradeneeded回调
也就是说多次open数据库,没有依次进行close,会导致真正的version发生改变时,也不执行onupgradeneeded回调
操作同一数据库不同表
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>同一个页面操作同一个数据库中的不同表</title>
<style>
.list {
margin-top: 40px;
}
.box {
border: 1px solid #ccc;
padding: 50px;
}
</style>
</head>
<body>
<div id="list">
<div class="student box">
<p>student表</p>
<div>id: <input class="id" /></div>
<br />
<div>str: <input class="str" /></div>
<br />
<button class="add">新增数据</button>
<button class="search">查找数据</button>
<button class="romove">删除数据</button>
<button class="update">更新数据</button>
<button class="delete">删除表</button>
<br />
</div>
<div class="teacher box">
<p>teacher表</p>
<div>id: <input class="id" /></div>
<br />
<div>str: <input class="str" /></div>
<br />
<button class="add">新增数据</button>
<button class="search">查找数据</button>
<button class="romove">删除数据</button>
<button class="update">更新数据</button>
<button class="delete">删除表</button>
<br />
</div>
</div>
<br />
<button id="deleteBase">删除库</button>
<script type="module">
import IndexedDB from './indexedDB.js'
// 不同数据库之间不需要open和关闭连接,也能够实现增删改查
// 同一个数据库操作不同表格,需要等待上一个表操作close才能再次open下一个表,只有open之后才可以进行表格内容的增删改查
// 注意数据库的版本version,添加操作和删除表操作,会导致version发生变化(每次操作数据表,都需要在open事触发版本更新)
const studentbaseTable = {
baseName: 'school', // 数据库名称
sheetName: 'studentTable', // 数据表名称
keyName: 'id', // 主键
indexArr: [{ name: 'str' }] // 索引
}
const teacherbaseTable = {
baseName: 'school', // 数据库名称
sheetName: 'teacherTable', // 数据表名称
keyName: 'id', // 主键
indexArr: [{ name: 'str' }], // 索引
}
let teacher = null
let maxVersion = null
const student = new IndexedDB(studentbaseTable)
student.isReady().then(() => {
student.destroy()
teacher = new IndexedDB(teacherbaseTable)
}).then(() => {
return teacher.isReady()
}).then(() => {
student.version = teacher.version
maxVersion = teacher.version
console.log(maxVersion)
teacher.destroy()
})
document.querySelector("#list .student .add").onclick = () => {
const id = document.querySelector("#list .student .id").value
const str = document.querySelector("#list .student .str").value
if(id !== "" && str !== "") {
// teacher.destroy()
student.open(maxVersion).then(() => {
return student.add({
id,
str
})
// 新增可能会改变版本号
}).then(() => {
maxVersion = student.version
confirm('新增数据成功, id为' id)
student.destroy()
}).catch(() => {
confirm('新增数据失败, id为' id)
student.destroy()
})
} else {
confirm("主键和str不能为空")
}
}
document.querySelector("#list .student .search").onclick = () => {
const id = document.querySelector("#list .student .id").value
if(id !== "") {
student.open(maxVersion).then(() => {
return student.read(id)
}).then(res => {
console.log('读取的数据为', res)
confirm('str: ' res.str)
student.destroy()
})
} else {
confirm("根据主键查找,主键不能为空")
}
}
document.querySelector("#list .student .romove").onclick = () => {
const id = document.querySelector("#list .student .id").value
if(id !== "") {
student.open(maxVersion).then(() => {
return student.remove(id)
}).then(res => {
confirm('student删除成功')
student.destroy()
}).catch(e => {
confirm('student删除失败')
})
} else {
confirm("根据主键删除,主键不能为空")
}
}
document.querySelector("#list .student .update").onclick = () => {
const id = document.querySelector("#list .student .id").value
const str = document.querySelector("#list .student .str").value
if(id !== "" && str !== "") {
// teacher.destroy();
student.open(maxVersion).then(() => {
return student.update({
id,
str
})
}).then(() => {
student.destroy()
confirm('更新成功')
}).catch(() => {
confirm('更新失败')
})
} else {
confirm("主键和str不能为空")
}
}
// 进行增删改查之后删除操作很慢进行
document.querySelector("#list .student .delete").onclick = () => {
student.open(maxVersion).then(() => {
return student.destroy()
}).then(() => {
return student.deleSheet(maxVersion 1)
}).then(() => {
maxVersion = student.version
// student.destroy()
confirm('student表删除成功')
}).catch((e) => {
confirm('删除表失败')
console.error(e)
})
}
document.querySelector("#list .teacher .add").onclick = () => {
const id = document.querySelector("#list .teacher .id").value
const str = document.querySelector("#list .teacher .str").value
if(id !== "" && str !== "") {
console.log(maxVersion)
teacher.open(maxVersion).then(() => {
return teacher.add({
id,
str
})
// 新增可能会改变版本号
}).then(() => {
maxVersion = teacher.version
confirm('新增数据成功, id为' id)
teacher.destroy()
}).catch(() => {
confirm('新增数据失败, id为' id)
teacher.destroy()
})
} else {
confirm("主键和str不能为空")
}
}
document.querySelector("#list .teacher .search").onclick = () => {
const id = document.querySelector("#list .teacher .id").value
if(id !== "") {
// student.destroy();
teacher.open(maxVersion).then(() => {
return teacher.read(id)
// 新增可能会改变版本号
maxVersion = teacher.version
}).then(res => {
console.log('读取的数据为', res)
confirm('str: ' res.str)
}).then(() => {
teacher.destroy()
})
} else {
confirm("根据主键查找,主键不能为空")
}
}
document.querySelector("#list .teacher .romove").onclick = () => {
const id = document.querySelector("#list .teacher .id").value
if(id !== "") {
teacher.open(maxVersion).then(() => {
return teacher.remove(id)
}).then(res => {
confirm('teacher删除成功')
teacher.destroy()
}).catch(e => {
confirm('teacher删除失败')
})
} else {
confirm("根据主键删除,主键不能为空")
}
}
document.querySelector("#list .teacher .update").onclick = () => {
const id = document.querySelector("#list .teacher .id").value
const str = document.querySelector("#list .teacher .str").value
if(id !== "" && str !== "") {
teacher.open(maxVersion).then(() => {
return teacher.update({
id,
str
})
}).then(() => {
teacher.destroy()
confirm('更新成功')
}).catch(() => {
confirm('更新失败')
})
} else {
confirm("主键和str不能为空")
}
}
document.querySelector("#list .teacher .delete").onclick = () => {
teacher.open(maxVersion).then(() => {
return teacher.destroy()
}).then(() => {
return teacher.deleSheet(maxVersion 1)
}).then(() => {
maxVersion = teacher.version
confirm('teacher表删除成功')
}).catch((e) => {
confirm('删除表失败')
console.error(e)
})
}
document.querySelector("#deleteBase").onclick = () => {
student.open(maxVersion).then(() => {
student.deleDataBase('school').then(() => {
confirm('删除库成功')
}).catch((e) => {
confirm('删除库失败')
console.error(e)
})
})
}
</script>
</body>
</html>
效果如图
2.为什么isReady()可传参,可不传参,什么情况适合传参?
这里可以解释这个疑问,同一个数据库中,多张表操作,会导致version版本号发生变化,所以需要页面维护这个版本号变量,当从一个表创建好之后,再创建另一个表时,需要升级版本号
所以当存在一个页面出现同一个数据库,多张表的情况就需要维护这个最高的版本号是多少了
当然,刷新页面这个版本号这个变量就会销毁,但是没关系,我们每次isReady()的时候,能够读取到最新的version变量
当一个页面操作同一个数据库中的不同表时,就需要维护version,所以也就这种情况需要传参,不然拿不到最新的version值,就会出现如下报错
dataBase打开报错 DOMException: The requested version (11) is less than the existing version (12).
并且删除数据库之后,下次依旧还能继续创建数据表再次进行数据添加和删除操作
操作不同数据库不同表
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>同一个页面操作不同数据库中的不同表</title>
<style>
.list {
margin-top: 40px;
}
.box {
border: 1px solid #ccc;
padding: 50px;
}
</style>
</head>
<body>
<div id="list">
<div class="student box">
<p>ss库中student表</p>
<div>id: <input class="id" /></div>
<br />
<div>str: <input class="str" /></div>
<br />
<button class="add">新增数据</button>
<button class="search">查找数据</button>
<button class="romove">删除数据</button>
<button class="update">更新数据</button>
<button class="delete">删除表</button>
<br />
</div>
<div class="teacher box">
<p>tt库teacher表</p>
<div>id: <input class="id" /></div>
<br />
<div>str: <input class="str" /></div>
<br />
<button class="add">新增数据</button>
<button class="search">查找数据</button>
<button class="romove">删除数据</button>
<button class="update">更新数据</button>
<button class="delete">删除表</button>
<br />
</div>
</div>
<br />
<button id="deleteBase_ss">删除ss库</button>
<button id="deleteBase_tt">删除tt库</button>
<script type="module">
import IndexedDB from '../util/indexedDB.js'
// 不同数据库之间不需要open和关闭连接,也能够实现增删改查
// 同一个数据库操作不同表格,需要等待上一个表操作close才能再次open下一个表,只有open之后才可以进行表格内容的增删改查
// 注意数据库的版本version,添加操作和删除表操作,会导致version发生变化(每次操作数据表,都需要在open事触发版本更新)
const studentbaseTable = {
baseName: 'ss', // 数据库名称
sheetName: 'studentTable', // 数据表名称
keyName: 'id', // 主键
indexArr: [{ name: 'str' }] // 索引
}
const teacherbaseTable = {
baseName: 'tt', // 数据库名称
sheetName: 'teacherTable', // 数据表名称
keyName: 'id', // 主键
indexArr: [{ name: 'str' }], // 索引
}
let teacher = null
// let maxVersion = null
const student = new IndexedDB(studentbaseTable)
student.isReady().then(() => {
student.destroy()
teacher = new IndexedDB(teacherbaseTable)
}).then(() => {
return teacher.isReady()
}).then(() => {
teacher.destroy()
})
document.querySelector("#list .student .add").onclick = () => {
const id = document.querySelector("#list .student .id").value
const str = document.querySelector("#list .student .str").value
if(id !== "" && str !== "") {
// teacher.destroy()
student.isReady().then(() => {
return student.add({
id,
str
})
// 新增可能会改变版本号
// maxVersion = student.version
}).then(() => {
confirm('新增数据成功, id为' id)
student.destroy()
}).catch(() => {
confirm('新增数据失败, id为' id)
student.destroy()
})
} else {
confirm("主键和str不能为空")
}
}
document.querySelector("#list .student .search").onclick = () => {
const id = document.querySelector("#list .student .id").value
if(id !== "") {
student.isReady().then(() => {
return student.read(id)
}).then(res => {
console.log('读取的数据为', res)
confirm('str: ' res.str)
student.destroy()
}).catch(() => {
student.destroy()
confirm('查找数据失败')
})
} else {
confirm("根据主键查找,主键不能为空")
}
}
document.querySelector("#list .student .romove").onclick = () => {
const id = document.querySelector("#list .student .id").value
if(id !== "") {
student.isReady().then(() => {
return student.remove(id)
}).then(res => {
confirm('student删除成功')
student.destroy()
}).catch(e => {
student.destroy()
confirm('student删除失败')
})
} else {
confirm("根据主键删除,主键不能为空")
}
}
document.querySelector("#list .student .update").onclick = () => {
const id = document.querySelector("#list .student .id").value
const str = document.querySelector("#list .student .str").value
if(id !== "" && str !== "") {
// teacher.destroy();
student.isReady().then(() => {
return student.update({
id,
str
})
}).then(() => {
student.destroy()
confirm('更新成功')
}).catch(() => {
student.destroy()
confirm('更新失败')
})
} else {
confirm("主键和str不能为空")
}
}
// 进行增删改查之后删除操作很慢进行
document.querySelector("#list .student .delete").onclick = () => {
student.isReady().then(() => {
return student.destroy()
}).then(() => {
return student.deleSheet(student.version 1)
}).then(() => {
// maxVersion = student.version
confirm('student表删除成功')
}).catch((e) => {
confirm('删除表失败')
console.error(e)
})
}
document.querySelector("#list .teacher .add").onclick = () => {
const id = document.querySelector("#list .teacher .id").value
const str = document.querySelector("#list .teacher .str").value
if(id !== "" && str !== "") {
teacher.isReady().then(() => {
return teacher.add({
id,
str
})
}).then(() => {
confirm('新增数据成功, id为' id)
teacher.destroy()
}).catch(() => {
confirm('新增数据失败, id为' id)
teacher.destroy()
})
} else {
confirm("主键和str不能为空")
}
}
document.querySelector("#list .teacher .search").onclick = () => {
const id = document.querySelector("#list .teacher .id").value
if(id !== "") {
// student.destroy();
teacher.isReady().then(() => {
return teacher.read(id)
// 新增可能会改变版本号
// maxVersion = teacher.version
}).then(res => {
console.log('读取的数据为', res)
confirm('str: ' res.str)
}).then(() => {
teacher.destroy()
}).catch(() => {
teacher.destroy()
confirm('查找数据失败')
})
} else {
confirm("根据主键查找,主键不能为空")
}
}
document.querySelector("#list .teacher .romove").onclick = () => {
const id = document.querySelector("#list .teacher .id").value
if(id !== "") {
teacher.isReady().then(() => {
return teacher.remove(id)
}).then(res => {
confirm('teacher删除成功')
teacher.destroy()
}).catch(e => {
teacher.destroy()
confirm('teacher删除失败')
})
} else {
confirm("根据主键删除,主键不能为空")
}
}
document.querySelector("#list .teacher .update").onclick = () => {
const id = document.querySelector("#list .teacher .id").value
const str = document.querySelector("#list .teacher .str").value
if(id !== "" && str !== "") {
teacher.isReady().then(() => {
return teacher.update({
id,
str
})
}).then(() => {
teacher.destroy()
confirm('更新成功')
}).catch(() => {
teacher.destroy()
confirm('更新失败')
})
} else {
confirm("主键和str不能为空")
}
}
document.querySelector("#list .teacher .delete").onclick = () => {
teacher.isReady().then(() => {
return teacher.destroy()
}).then(() => {
return teacher.deleSheet(teacher.version 1)
}).then(() => {
// maxVersion = teacher.version
// teacher.destroy();
confirm('teacher表删除成功')
}).catch((e) => {
confirm('删除表失败')
console.error(e)
})
}
document.querySelector("#deleteBase_ss").onclick = () => {
student.isReady().then(() => {
student.deleDataBase('ss').then(() => {
confirm('删除ss库成功')
}).catch((e) => {
confirm('删除ss库失败')
console.error(e)
})
})
}
document.querySelector("#deleteBase_tt").onclick = () => {
teacher.isReady().then(() => {
teacher.deleDataBase('tt').then(() => {
confirm('删除tt库成功')
}).catch((e) => {
confirm('删除tt库失败')
console.error(e)
})
})
}
</script>
</body>
</html>
3.为什么新增和修改数据时,需要先判断是否有表?
因为当open表的回调还未执行完成, 可能页面一初始化完成,就立刻去查询页面是否存在某个数据, 当不存在这个数据时, 再去新增这个数据, 这张情况下, 就会出现未成功建表, 可以使用dataBase.objectStoreNames.contains(this.dataSheetName)判断
如果未建表, 就去操作数据表, 肯定是会报错的, 错误如下:
DOMException: Failed to execute ‘transaction’ on ‘IDBDatabase’: One of the specified object stores was not found.
数据库未open连接报错如下:
DOMException: Failed to execute ‘transaction’ on ‘IDBDatabase’: The database connection is closing.
4.为什么删除和查询,不需要再次更新版本
能够发现, 新增和更新, 本质都是调用add方法, 而add方法中, 判断未建表时, 会重新建表
原因其实也很简单, 新增数据, 那说明这个数据需要新增或者更新, 那这个时候未建表, 肯定会报错, 但是如果一直未建表, 就一直不能新增数据, 这就会导致功能出错了, 所以这里做了一点特殊处理.
初始化页面进行表操作
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="tableA">
<p>tableA</p>
<div>id: <input class="id" /></div>
<br />
<div>str: <input class="str" /></div>
<br />
<button class="add">新增数据</button>
<button class="search">查询数据</button>
<button class="delete">删除数据</button>
<button class="remove">删除表</button>
</div>
<div id="tableB">
<p>tableB</p>
<div>id: <input class="id" /></div>
<br />
<div>str: <input class="str" /></div>
<br />
<button class="add">新增数据</button>
<button class="search">查询数据</button>
<button class="delete">删除数据</button>
<button class="remove">删除表</button>
</div>
<script type="module">
import IndexedDB from './indexedDB.js'
const studentbaseTable = {
baseName: 'ss', // 数据库名称
sheetName: 'tableA', // 数据表名称
keyName: 'id', // 主键
indexArr: [{ name: 'str' }] // 索引
}
const teacherbaseTable = {
baseName: 'tt', // 数据库名称
sheetName: 'tableB', // 数据表名称
keyName: 'id', // 主键
indexArr: [{ name: 'str' }], // 索引
}
let teacher = null
// let maxVersion = null
const student = new IndexedDB(studentbaseTable)
student.isReady()
.then(() => {
return student.add({ id: 123, str: "test"})
})
.then(() => {
return student.read(123)
})
.then(() => {
student.destroy()
teacher = new IndexedDB(teacherbaseTable)
})
.then(() => {
return teacher.isReady()
})
.then(() => {
return teacher.add({ id: 456, str: "asd"})
})
.then(() => {
return teacher.read(456)
})
.then(() => {
teacher.destroy()
})
</script>
</body>
</html>
最后附上本文的代码仓库:
https://gitee.com/bluelightsky/indexedBD
https://github.com/bluelightsky/indexedDB-demo
当然如果还需要满足阻塞等更加复杂的操作,推荐使用indexedDB的库,这里我推荐idb,参考文章中有介绍idb库使用的文章
参考文章:
英文:
https://hackernoon.com/use-indexeddb-with-idb-a-1kb-library-that-makes-it-easy-8p1f3yqq中文: https://blog.csdn.net/ioriogami/article/details/128438731
https://zh.javascript.info/indexeddb#da-kai-shu-ju-ku
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhfhchbe
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01 -
怎样阻止微信小程序自动打开
PHP中文网 06-13