likes
comments
collection
share

一文看懂 IndexedDB

作者站长头像
站长
· 阅读数 80

一文看懂 IndexedDB

前言

之前在公司项目中使用过一次 IndexedDB,场景是保存生成 Blob 格式的视频到 IndexedDB,用户刷新后依然可以观看本地录制视频或重新上传视频。但当时需求紧急,还没怎么系统学习过,这次来重新拾起,从 IndexedDB 一些基础 API 学起。

为什么需要 IndexedDB

随着前端技术的发展和浏览器功能的不断增强,越来越多复杂的交互和业务需求需要前端来实现,其中一种常用的优化方式就是本地存储数据,减少从服务器获取数据,直接从本地获取数据也能提高页面渲染的速度。但先前的数据储存方案都不适合储存大量结构化数据,如 localStorage 容量通常在 10MB 以内(各家浏览器不同),储存的格式数据也有限,且不提供搜索功能,不能建立自定义的索引;因此需要一种新的解决方案,那就是 IndexedDB。

IndexedDB 是什么

IndexedDB 就是浏览器提供的本地数据库,它提供了一些底层 API,用于在客户端存储大量的结构化数据(也包括文件/二进制大型对象),并提供查找接口,还能建立索引。

IndexedDB 的特点

以下内容部分引用自:浏览器数据库 IndexedDB 入门教程

  • 键值对储存。IndexedDB 内部采用对象仓库存放数据。所有类型的数据都可以直接存入,包括 JavaScript 对象。对象仓库中,数据以"键值对"的形式保存,每一个数据记录都有对应的主键,主键是独一无二的,不能有重复,否则会抛出一个错误。

  • 异步操作。IndexedDB 操作时不会锁死浏览器,用户依然可以进行其他操作,这与 LocalStorage 形成对比,后者的操作是同步的。异步设计是为了防止大量数据的读写,拖慢网页的表现。

  • 支持事务。IndexedDB 支持事务(transaction),这意味着一系列操作步骤之中,只要有一步失败,整个事务就都取消,数据库回滚到事务发生之前的状态,不存在只改写一部分数据的情况。

数据库事务(transaction)是访问并可能操作各种数据项的一个数据库操作序列,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。事务由事务开始与事务结束之间执行的全部数据库操作组成。

  • 同源限制。IndexedDB 受到同源限制,每一个数据库对应创建它的域名。网页只能访问自身域名下的数据库,而不能访问跨域的数据库。

  • 储存空间大。IndexedDB 的储存空间比 localStorage 大得多,一般来说不少于 250MB,甚至没有上限(取决于硬盘大小)。

  • 支持二进制储存。IndexedDB 不仅可以储存字符串,还可以储存二进制数据(ArrayBuffer 对象和 Blob 对象)。

浏览器兼容性

一文看懂 IndexedDB

判断当前浏览器是否支持 IndexedDB,可用如下代码:

window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB

if (!window.indexedDB) {
  alert('你的浏览器不支持IndexedDB')
}

操作流程

打开/连接数据库

打开数据库使用 indexedDB.open() 方法

const request = window.indexedDB.open(name, version)
  • name:数据库名称。如果指定的数据库不存在,就会新建数据库。
  • version:指定数据库版本(整数)。如果省略,打开已有数据库时,默认为当前版本;新建数据库时,默认为 1。当你想要更改数据库格式(比如增加对象存储,非增加记录),必须指定更高版本。
  • 返回值:IDBOpenDBRequest 对象,这个对象通过三种事件 error、success、upgradeneeded,处理打开数据库的操作结果。
const request = window.indexedDB.open('myIndexedDB')

// success 事件表示成功打开数据库
request.onsuccess = (event) => {
  db = event.target.result
  console.log('打开 IndexedDB 成功')
}
// error 事件表示打开数据库失败
request.onerror = (event) => {
  console.log('打开 IndexedDB 失败')
}
// 如果指定的版本号,大于数据库的实际版本号,就会发生数据库升级事件 upgradeneeded request.onupgradeneeded = function (e) {
  console.log('当前数据库版本号为' + e.newVersion)
}

一文看懂 IndexedDB

需要特别一提的是 onupgradeneeded 方法,该方法表示数据库已准备就绪,但其版本已过时。当打开数据库不存在时会新建数据库,这个时候也会触发该方法,因此在该方法下可以执行初始化操作。

而如果本地数据库版本低于 open 方法中指定的版本,代表版本过时,这时会触发 onupgradeneeded 方法

IndexedDB API 中不允许数据库中的数据仓库在同一版本中发生变化,所以需要在 indexedDB.open 方法中传入新的版本号来更新版本,避免在同一版本中重复修改数据库。

const request = window.indexedDB.open('myIndexedDB', 2) // 第一次打开版本默认为1,这次指定改为2
request.onupgradeneeded = function (e) {
  console.log('当前数据库版本号为' + e.newVersion) // 当前数据库版本号为2
}

关闭数据库

打开数据库返回的 IDBOpenDBRequest 对象,可以使用该对象 close() 方法来关闭数据库

let db
const request = window.indexedDB.open('myIndexedDB')
request.onsuccess = (event) => {
  db = event.target.result
  console.log('打开 IndexedDB 成功')
  db.close()
  console.log('关闭数据库成功')
}

删除数据库

indexedDB.deleteDatabase(name) 删除指定名称的数据库

window.indexedDB.deleteDatabase('myIndexedDB')

新建对象仓库(表)

建完数据库后,我们需要一张表来存储信息,使用到的是 IDBDatabase.createObjectStore()

const objectStore = IDBDatabase.createObjectStore(name)
const objectStore = IDBDatabase.createObjectStore(name, options)
  • name:被创建的 object store 的名称
  • options:
    • keyPath:存储使用的主键
    • autoIncrement:是否使用自动递增的整数作为主键,默认为 false
  • 该方法创建并返回一个新的 object store 或 index

示例如下:

request.onupgradeneeded = function (e) {
  const objectStore = db.createObjectStore('persons', { keyPath: 'id' })
}

一文看懂 IndexedDB

新建索引

IDBObject.createIndex 通过数据对象的某个属性来创建索引,在数据库中进行检索时,只能通过被设为索引的属性进行检索

IDBObject.createIndex(indexName, keyPath, options)
  • indexName:索引名称
  • keyPath:索引所在的属性
  • options:配置对象。常用参数是 unique,表示该字段值是否唯一不重复

示例如下:

request.onupgradeneeded = function (e) {
  db = e.target.result
  // 创建存储对象
  const objectStore = db.createObjectStore('persons', { keyPath: 'id' })
  // 创建索引
  objectStore.createIndex('name', 'name')
  objectStore.createIndex('age', 'age')
}

一文看懂 IndexedDB

新建数据

新增数据指的是向对象仓库(表)写入数据记录,使用 IDBObjectStore.add() 方法。

数据库的操作都是基于事务(transaction)来进行的,所以对于增删改查都会用到 IDBTransaction

request.onsuccess = (event) => {
  db = event.target.result

  const items = [{ id: 1, name: '张三', age: 18 }]
  // 创建一个读写事务
  const transaction = db.transaction(['persons'], 'readwrite')
  // 获取对象存储区,返回 IDBObjectStore 对象
  const objectStore = transaction.objectStore('persons')
  // 向表格写入一条记录
  const request = objectStore.add(items[0])

  request.onsuccess = function (e) {
    console.log('写入数据成功')
  }

  request.onerror = function (e) {
    console.log('写入数据失败')
  }
}

一文看懂 IndexedDB

更新数据

更新数据使用的是 IDBObjectStore.put() 方法,用于更新一条给定的数据库记录,如果给出的值不存在,则插入一个新的记录

function update() {
  // 创建一个读写事务
  const transaction = db.transaction(['persons'], 'readwrite')
  // 获取对象存储区
  const objectStore = transaction.objectStore('persons')
  // 更新数据
  const request = objectStore.put({ id: 1, name: '李四', age: 30 })

  request.onsuccess = function (e) {
    console.log('更新数据成功')
  }

  request.onerror = function (e) {
    console.log('更新数据失败')
  }
}

一文看懂 IndexedDB

读取数据

数据库读取使用到的 API 是 IDBCursor,IDBCursor  接口表示一个游标,用于遍历或迭代数据库中的多条记录。

使用的时候可以使用存储对象的 openCursor() 方法来打开游标

function read() {
  // 创建一个读写事务
  const transaction = db.transaction(['persons'])
  // 获取对象存储区
  const objectStore = transaction.objectStore('persons')
  // 读取数据
  objectStore.openCursor().onsuccess = function (e) {
    const cursor = e.target.result // 数据对象
    if (cursor) {
      console.log('读取数据成功:', cursor.value)
      // 游标没有遍历完则继续遍历
      cursor.continue()
    } else {
      // 如果全部遍历完毕...
    }
  }
}

一文看懂 IndexedDB

也可以使用 IDBObjectStore.get(),传入标识要检索的记录的键或键范围:

// 创建一个读写事务
const transaction = db.transaction(['persons'])
// 获取对象存储区
const objectStore = transaction.objectStore('persons')
// 获取存储键值为1的存储对象
const objectStoreRequest = objectStore.get(1)
objectStoreRequest.onsuccess = function (e) {
  // 当前数据
  const record = objectStoreRequest.result
  console.log('record', record)
}

删除数据

删除记录使用的是 IDBObjectStore: delete(),传入要删除的键或者IDBKeyRange范围

如下删除 id 为 1 的一条记录

db.transaction(['persons'], 'readwrite').objectStore('persons').delete(1)

应用场景

  1. 离线 Web 应用程序
  2. 缓存大量数据与数据同步。如因存储容量或格式不满足时,也可考虑 IndexedDB
  3. 复杂的查询。IndexedDB 支持复杂的键值查询

结语

本文作为 IndexedDB 浅显入门篇,更多 IndexedDB 用法并没提及,各位有兴趣深入的可以自己去了解看看。

参考文章

转载自:https://juejin.cn/post/7249386837369159735
评论
请登录