IndexDB是适用于浏览器的文档数据库,它有以下特点:
- 兼容所有现代的浏览器
- 支持事务以及版本控制
- 支持无限数量的数据。很多浏览器会限定localStorage或者sessionStorage的存储空间为2M到10M
- IndexDB是异步的API,它不会阻塞浏览器UI的渲染
下面介绍下它的使用。
安装依赖包idb
需要把idb的js库添加到依赖。有几种方式添加idb到依赖。
yarn
yarn add idb
npm
npm install idb --save
安装后可以使用webpack等工具把它添加到页面,或者在页面直接引用
<script src="./node_modules/idb/lib/idb.js"></script>
在开始使用indexdb前,建议先检测浏览器是否支持indexdb。如下:
(() => {
'use strict'
if (!('indexedDB' in window)) {
console.warn('IndexedDB not supported')
return
}
//...IndexedDB code
})()
创建IndexDB数据库
使用idb.open()创建indexdb数据库,如下:
const name = 'mydbname'
const version = 1 //版本
idb.open(name, version, upgradeDb => {})
idb.open()的第三个参数是可选的,它是用于升级已经安装的indexdb,只有在version大于已经安装的indexdb时才会调用。所以我们的表结构发生变化,就可以通过升级版本号,然后再回调函数upgradeDb里做升级。
创建对象存储
indexDB使用以下语法来创建对象存储:
db.createObjectStore('storeName', options)
其中第一个参数为对象的存储名,第二个参数为选项,可选。
示例:
const dbPromise = idb.open('mydb', 1, (upgradeDB) => {
upgradeDB.createObjectStore('store1')
})
.then(db => console.log('success'))
如果之前已经安装了一个旧版本的indexdb,那么你可以在升级回调里把一个新的对象存储添加进去。
const dbPromise = idb.open('mydb', 3, (upgradeDB) => {
switch (upgradeDB.oldVersion) {
case 0: //旧版本为0时,表示是一个新的indexdb
//在版本1创建store1
upgradeDB.createObjectStore('store1')
case 1:
// 版本2新增store1
upgradeDB.createObjectStore('store2', { keyPath: 'name' })
}
})
.then(db => console.log('success'))
createObjectStore的第二个参数里的选项keyPath,是用来设置索引键,指定为索引的键需要时唯一的,即所有数据项此键的值是不同的。
键可以设置为自增,你不需要去指定它,IndexedDB会为自动创建它:
upgradeDb.createObjectStore('notes', { autoIncrement: true })
合起来指定一个索引键,并设置为自增:
upgradeDb.createObjectStore('notes', {
keyPath: 'id',
autoIncrement: true
})
索引
索引可以在idb.open的升级回调函数里添加,如下:
const dbPromise = idb.open('dogsdb', 1, (upgradeDB) => {
const dogs = upgradeDB.createObjectStore('dogs')
dogs.createIndex('nameIndex', 'name', { unique: false })
})
createIndex三个参数:
- indexName:第一个参数为索引名
- field:第二个参数为索引对应的属性
- options:选项
在第三个参数选项unique,用来指示所以是否是唯一的。
除了在新建objectStore时添加索引,也可以在已经创建好的ojbectStore补充添加索引,如下:
const dbPromise = idb.open('dogsdb', 1, (upgradeDB) => {
const dogs = upgradeDB.transaction.objectStore('dogs')
dogs.createIndex('name', 'name', { unique: false })
})
其中upgradDb.transation.objectStore()用来获取之前已经创建的objectStore。
检查Store是否已存在
升级数据库结构,特别是创建ObjectStore,常常需要判断Store是否已经创建了。可以使用objectStoreNames来获取所有的objectStore的名称列表。
if (!upgradeDb.objectStoreNames.contains('store3')) {
upgradeDb.createObjectStore('store3')
}
IndexDB的删除操作
删除数据库
使用idb.delete()来删除数据库,如下:
idb.delete('mydb').then(() => console.log('done'))
删除objectStore
删除objectStore的操作只能在idb.open()的升级回调里执行。如之前所说,idb.open()的升级回调只有在version大于已经安装的数据库版本才会调用。
const dbPromise = idb.open('dogsdb', 2, (upgradeDB) => {
upgradeDB.deleteObjectStore('old_store')
})
使用事务删除object store里的数据项:
const key = 232
dbPromise.then((db) => {
const tx = db.transaction('store', 'readwrite')
const store = tx.objectStore('store')
store.delete(key)
return tx.complete
})
.then(() => {
console.log('Item deleted')
})
新增/更新数据
添加数据项到objectStore,可以使用objectStore的put(val,key)方法,需要注意的是第一个参数为值,第二个参数为键。
在使用put方法前,先要获取objectStore对象。获取方法:
db.transaction().objectStore()
示例:
const dbPromise = idb.open("mydb",1);
dbPromise.then((db) => {
const val = 'hey!'
const key = 'Hello again'
const tx = db.transaction('store1', 'readwrite')
tx.objectStore('store1').put(val, key)
return tx.complete
})
.then(() => {
console.log('Transaction complete')
})
.catch(() => {
console.log('Transaction failed')
})
尽管indexDB提供了add()方法,但put()方法除了添加功能外,同时也是可以为更新操作,所有put是比较常用的新增或更新数据项的方法。
查询数据
1、使用get获取store里的指定的数据项
dbPromise.then(db => db.transaction('objs')
.objectStore('objs')
.get(123456))
.then(obj => console.log(obj))
2、使用getAll()获取所有的数据项
dbPromise.then(db => db.transaction('store1')
.objectStore('store1')
.getAll())
.then(objects => console.log(objects))
3、通过openCursor()使用游标迭代数据
dbPromise.then((db) => {
const tx = db.transaction('store', 'readonly')
const store = tx.objectStore('store')
return store.openCursor()
})
.then(function logItems(cursor) {
if (!cursor) { return }
console.log('cursor is at: ', cursor.key)
for (const field in cursor.value) {
console.log(cursor.value[field])
}
return cursor.continue().then(logItems)
})
.then(() => {
console.log('done!')
})
4、使用游标和边界来迭代数据项的子集
const searchItems = (lower, upper) => {
if (lower === '' && upper === '') { return }
let range
if (lower !== '' && upper !== '') {
range = IDBKeyRange.bound(lower, upper)
} else if (lower === '') {
range = IDBKeyRange.upperBound(upper)
} else {
range = IDBKeyRange.lowerBound(lower)
}
dbPromise.then((db) => {
const tx = db.transaction(['dogs'], 'readonly')
const store = tx.objectStore('dogs')
const index = store.index('age')
return index.openCursor(range)
})
.then(function showRange(cursor) {
if (!cursor) { return }
console.log('cursor is at:', cursor.key)
for (const field in cursor.value) {
console.log(cursor.value[field])
}
return cursor.continue().then(showRange)
})
.then(() => {
console.log('done!')
})
}
searchDogsBetweenAges(3, 10)