indexedDB简介
indexedDB是一种现代浏览器持久化数据存储解决方案, 可以把它理解成用户浏览器端的数据库,它可以存储包括文件/二进制大型对象blobs等大量结构化数据,通过索引来实现对数据的高性能检索
indexeddb内部的数据均是通过键值对的形式存储,所以不同于其他关系型数据库,不支持sql语句,indexeddb更类似于nosql数据库,通过提供的大量api来实现数据的增删改查
需要注意的是,因为indexedDB中的很多api涉及i/o操作,所以是异步的
作为一个新增的规范,indexedDB新增了许多概念
-
数据库
是一系列对象仓库的容器,同一个源下能存在多个数据库 -
版本
和其他数据库不同,indexedDB中的数据库具有版本的概念,每当该数据库的版本发生改变的时候就说明该数据库发生了更新,且版本只能递增不能递减 -
对象仓库
类似于其他数据库中的表,只不过这里的对象仓库存储的是键值对 -
数据记录
即对象仓库中保存的键值对。类似于其他数据库中数据表中的行,key即主键,主键可以是value里面的一个属性,也可以指定为一个递增的整数编号{ id: 1, name: "张三", age: 20, score: 90 }
这里的主键即value中的id -
索引
为了加速数据的检索,可以在对象仓库里面,为不同的属性建立索引,和其他数据库中的索引类似 -
事务
indexedDB中所有对数据的操作都需要通过事务来执行,事务具有两种模式:readwrite和read模式,这里的事务和其他数据库中的事务类似 -
游标
游标可以理解为一个指针,在通过游标来查询数据时,游标最开始会指向第一条符合条件的数据,然后一行一行向下移动,游标走到的地方便会返回这一行数据,此时我们便可对此行数据进行判断,是否满足条件
web SQL
在indexeddb推出之前,W3C所推荐的的浏览器大数据持久化解决方案其实是webSQL,和indexedDB不同,webSQL是关系型数据库,通过SQL来实现对数据的增删改查,目前W3C已经放弃了这个规范转而推荐indexedDB
废弃这个规范的原因也很简单,web SQL规范的制订陷入了僵局,目前实现的web SQL都是通过SQLite来实现,缺乏多样性的实现让这个规范的标准化无法推进,最终不得不停止制订web SQL规范
创建数据库
在indexedDB中我们创建一个数据库需要调用indexedDB提供的open方法
open方法需要传两个参数,dbName为数据库的名字,version为该数据库的版本
如果此前没有创建此数据库的话open方法会自动创建此数据库
如果此前已经创建了此数据库,在通过open方法打开的话version不能比此数据库的版本低
let db
const cmd = indexedDB.open(dbName, version)
open方法返回一个IDBDatabase对象,这个对象有三种事件可以监听:
-
onsuccess:数据库打开成功的回调
cmd.onsuccess = (event) => { console.log('数据库打开成功') db = event.target.result }
-
onerror:数据库打开失败的回调
cmd.onerror = (event) => { console.log('数据库打开失败') }
-
onupgradeneeded:数据库发生更新的回调
cmd.onupgradeneeded = (event) => { console.log('数据库数据库更新') db = event.target.result }
我们可以通过浏览器控制台来观察目前已经创建的数据库
创建对象仓库
我们通过open方法拿到了db对象,这个对象提供createObjectStore方法用来创建对象仓库
需要注意的是,这个方法只能在数据库更新的时候调用,即我们只能在数据库更新时创建对象仓库
const store = db.createObjectStore(storeName, options)
这个方法接收两个参数,storeName为要创建的对象仓库的名称,options为该对象仓库的相关配置,配置项有两个:
- keyPath:即该数据仓库的主键,如果为空或未指定,该对象仓库将使用out-of-line keys
- autoIncrement:即主键是否自增
新增数据
我们可以通过add方法来向指定的对象仓库中新增数据记录
const transaction = db.transaction(storeName, 'readwrite')
const store = transaction.objectStore(storeName)
const cmd = store.add(data)
cmd.onsuccess = (event) => {
console.log('更新数据成功')
}
cmd.onerror = (event) => {
console.log('更新数据失败')
}
transaction方法通过指定需要对那个对象仓库操作和该事务的类型来返回一个事务对象
objectStore方法通过指定对象仓库名称来获取指定的仓库对象
最后我们通过仓库对象的add方法来放入数据记录
仓库对象支持两种事件,onsuccess,onerror,具体见上
删除数据
我们可以通过delete方法来向指定的对象仓库中删除数据记录
const transaction = this.db.transaction(storeName, 'readwrite')
const store = transaction.objectStore(storeName)
const cmd = store.delete(key)
cmd.onsuccess = (event) => {
console.log('删除数据成功')
}
cmd.onerror = (event) => {
console.log('删除数据失败')
}
注意,在delete时我们需要显式的传入主键值
删除对象仓库
const store = db.deleteObjectStore(storeName)
删除数据库
const cmd = indexedDB.deleteDatabase(dbName)
cmd.onsuccess = (event) => {
console.log('数据库删除成功')
}
cmd.onerror = (event) => {
console.log('数据库删除失败')
}
更新数据
更新数据记录有两种方法,put和update
put方法可以通过传入的数据记录自动找到需要替换的数据记录,前提是当前对象仓库设置了keyPath,如果没有设置则需要显示的传入主键
const transaction = db.transaction(storeName, 'readwrite')
const store = transaction.objectStore(storeName)
const cmd = store.put(data)
cmd.onsuccess = (event) => {
console.log('更新数据成功')
}
cmd.onerror = (event) => {
console.log('更新数据失败')
}
如果是IDBCursor对象,即通过游标来读取数据的话我们可以使用update方法来修改读取的这一行数据
const request = cursor.update(updateData);
request.onsuccess = () => {
console.log("更新数据成功");
}
查询数据
在indexedDB中我们有多种查询数据的方法
-
get:通过key的形式
const transaction = db.transaction(storeName, 'readonly') const store = transaction.objectStore(storeName) const cmd = store.get(key) cmd.onsuccess = (event) => { console.log('查询数据成功') console.log(event.target.result) } cmd.onerror = (event) => { console.log('查询数据失败') }
-
getAll:获取全部数据
const transaction = db.transaction(storeName, 'readonly') const store = transaction.objectStore(storeName) const cmd = store.getAll() cmd.onsuccess = (event) => { console.log('查询数据成功') console.log(event.target.result) } cmd.onerror = (event) => { console.log('查询数据失败') }
-
openCursor:通过游标来获取
const transaction = db.transaction(storeName, 'readonly') const store = transaction.objectStore(storeName) const cursor = range ? store.openCursor(range, direction) : store.openCursor() const result = [] cursor.onsuccess = (event) => { const cursor = event.target.result if (cursor) { result.push(cursor.value) cursor.continue() } else { console.log('查询数据成功') }
需要注意的是,openCursor方法可以不传参数,当无参数时默认指向对象仓库中第一个数据记录,如果传值的话可以传两个值,range为一个IDBKeyRange对象,是对当前主键的一个约束,direction为游标的走向,默认为next
-
index:通过指定key-value获取
const transaction = db.transaction(storeName, 'readonly') const store = transaction.objectStore(storeName) const cmd = store.index(indexName).get(value) cmd.onsuccess = (event) => { console.log('查询数据成功') console.log(event.target.result) } cmd.onerror = (event) => { console.log('查询数据失败') reject(event.target.error) }
我们可以调用index方法,传入一个key,indexedDB会查找所有数据记录中包含这个键的数据记录,再通过get方法得到这个键为value的数据记录