IndexDB快速入门

时间:2022-06-01 20:48:19

IndexDB是适用于浏览器的文档数据库,它有以下特点:

  1. 兼容所有现代的浏览器
  2. 支持事务以及版本控制
  3. 支持无限数量的数据。很多浏览器会限定localStorage或者sessionStorage的存储空间为2M到10M
  4. 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)