IndexedDB学习笔记
前言
IndexedDB是html5标准引入的web数据持久化方案之一,现在浏览器大多按照标准对其进行了实现。
IndexedDB是一种底层API,用于再客户端存储大量的的结构化数据(也包括文件/二进制大型对象(blobs))。该API使用索引实现对数据的高性能搜索。
各浏览器支持:
大部分浏览器支持 version 为1的 indexedDB;
IndexedDB特点:
- 键值对存储。 IndexedDB内部采用对象仓库(object store)存放数据。所有类型的数据都可以直接存入,包括JavaScript对象。对象仓库中,数据以”键值对”的形式保存,每一个数据记录都有对应的主键,主键是独一无二的,不能有重复,否则会抛出一个错误。
- 异步。IndexedDB操作时不会锁死浏览器,用户依然可以进行其他操作。这与LocalStorage形成对比,后者的操作是同步的。异步设计是为了防止大量数据的读写,拖慢网页的标准。
- 支持事务。IndexedDB支持事务(transaction),这意味着一些列操作步骤之中,只要有一部失败,整个事务就都取消,数据库回滚到事务发生之前的状态,不存在只改写一部分数据的情况。
- 同源限制。IndexedDB受到同源限制,每一个数据库对应创建它的域名。网页只能访问自身域名下的数据库,而不能访问跨域的数据库。
- 存储空间大。IndexedDB的存储空间比LocalStorage大的多,一般来说不少于250MB,理论上没有上限。
- 支持二进制存储。IndexedDB不仅可以存储字符串,还可以存储二进制(ArrayBuffer对象和Blob对象)。
IndexedDB概念知识体系:
-
结构层面: database
-
database: 数据库,同域名下可以有多高数据库
-
数据库的连接
- 打开数据库使用
indexedDB.open()
方法。这个方法接受两个参数,第一个参数是字符串,表示数据库的名字databaseName
,如果指定的数据库不存在,就会新建数据库。第二个参数是整数,表示数据库的版本version
。如果省略,打开已有数据库时,默认为当前版本,新建数据库时,默认为1。
const request = window.indexedDB.open('mydb', 1); 复制代码
-
indexedDB.open()
方法会返回一个IDBRequest对象。这个对象通过三个时间error
,success
,upgradeneeded
,处理打开数据库的操作结果let db; request.onerror = e => { console.error('打开数据库发生错误!') } request.onsuccess = e => { // 通过事件对象 `e` 的target.result 属性,拿到数据库实例。 db = e.target.result console.log('打开数据库成功!') } request.onupgradeneeded = e => { // 如果指定的版本号,大于数据库的实际版本号,就会发生数据库升级事件 // db = e.target.result; // 通过事件对象 `e` 的target.result 属性,拿到数据库实例。 console.log('用于在加载具有更高版本号的数据库时更新数据库') } 复制代码
- 打开数据库使用
-
数据库的version(注意点):
- version 必须是大于0的正整数;
- 当需要对数据库的结构进行调整时,必须升级version,并且在 onupgradeneeded中调整结构;
- version 只能上升,不能下降;
-
-
-
存储层面:objectStore
-
objectStore: 仓库,每个数据库可以有多个objectStore;
-
objectStore是indexedDB的数据存储机制,和 SQL 的表的地位是一致的,每一条记录保护了 key 和 value;
-
key 是 value的标记值,我们通过key得到indexedDB中存储的对应值。要获得value,必须通过key来获得。key和value具有绑定关系,key相当于value的缩写、别名、标记。
-
indexedDB的key 有两种形态: inline 和 outline;
-
inline key是指key被包含在value中,例如你存储的value是对象,就可以将key包含在 value中。
-
outline key 则是不被包含在value中,例如你存储的是字符串或者ArrayBuffer,就不可能在value中包含key,这种情况下通过开启autoIncrement来实现的。
-
-
-
-
– keyPath 是指在对象中,获取一个节点指的属性链式方法的字符串表达式;
- keyPath的作用是读取key。key是value的keyPath的值。
- 在indexedDB中,keyPath是一个配置选项,在创建objectStore和index是传入,一经确定,不能再改。
- 对于outline key的情况,在创建objectStore时,我们不传keyPath,开启autoIncrement功能。
- value 可以是 String,Date,Object,Array,File,Blob,ImageData,Null,Number等,支持存储ArrayBuffer,不能存Function、类的实例;
- objectStore的类型(个人进行分类的,原则上没有这样的):
- 对象型:`db.createObjectStore('mystore', {keyPath: 'id'})`
- 非对象型 `db.createObjectStore('mystore', {autoIncrement: true})`
- 混合型 `db.createObjectStore('mystore', {keyPath: 'id', autoIncrement: true})`
- autoIncrement是indexedDB的可以自动生成机制(自动增长);
- 当一个objectStore开启该功能后,在添加一条记录是,value不存在keyPath情况下,不会报错,而是被自动添加;
- 当keyPath 和 autoIncrement两个配置选项同时传入时,一个被存入的对象如果不存在keyPath,这个对象会自动被添加该keyPath,对象被改写;
```javascript
const request = window.indexedDB.open('mydb', 1)
request.onupgradeneeded = e => {
const db = e.tareget.result
if (!db.objectStoreNames.contains('mystore')) { // 先判断一下这个对象仓库(表),是否存在,不存在在新建
db.createObjectStore('mystore', {keyPath: 'id'})
}
}
request.onsuccess = e => {
const db = e.target.result
// .....
}
```
- index(索引):
- 索引是用于查询数据的补充方式;
- 索引(存储空间)本质上是一种特殊的objectStore。也有自己的name,keyPath,key和value。特殊在于索引值可有一定的逻辑约束,例如 `unique`;
- idnex 依附于 objectStore而存在。是objectStore的查询补充方式。
- index的key 和 value 全部来自于objectStore。key为某条记录的keyPath(index.keyPath)的值,value为这条记录的key(objectStore.key)。
- index中的一条记录对应objectStore中的一条记录。当objectStore的记录发生变化时,index中的记录会被自动污染(自动更新)。
复制代码
- 默认情况下,index的Key是可以重复的;
- 如果你建立的索引传入了unique参数,该索引的keyPath被用于检查存入的值的对应的keyPath是否已经有值,如果objectStore中存在的该KeyPath的值为将要存入的值的该keyPath,那么会抛出错误。
- 创建index索引:
```javascript
let request = window.indexedDB.open('mydb', 1)
request.onupgradeneeded = e => {
let db = e.target.result
let objectStore = db.createObjectStore('mystore', {keyPath: 'id'})
objectStore.createIndex('price', 'book.price', {unique: false}) // 创建index索引
}
```
- 更新索引:
```javascript
request.onupgradeneeded = e => {
const objectStore = e.target.transaction.objectStore('mystore')
const indexNames = objectStore.indexNames
if (indexNames.contains('name')) {
objectStore.deleteIndex('name') // 删除后重新创建; 删除索引不会影响到objectStore中的数据
}
objectStore.createIndex('name', 'name', { unique: false })
}
```
- 使用索进行查询:
```javascript
// 获取到objectStore,通过index去查询 indexName
const request = objectStore.index('indexName').get('indexKey')
```
复制代码
-
原子层面:key-value
- key-value: 记录,objectStore就像一个对象,以key-value的形式存储所有数据
-
应用层面:index、cursor
-
cursor游标:
-
游标 是indexedDB的遍历工具。
-
本质上,它是一个迭代器,可以通过
.value
读取当前值,通过.continue
继续迭代。 -
游标有方向,可以从最后的记录往前遍历。
// 使用游标的示例 let transaction = db.transaction(['myObjectStore'], 'readonly') // 发起事务 let objectStore = transaction.objectStore('myObjectStore') let request = objectStore.openCursor() let results = [] request.onsuccess = e => { let cursor = e.target.result if (cursor) { results.push(cursor.value) cursor.continue() } else { // 所有的object都在results里面 } } 复制代码
- 游标的作用:遍历数据的需要;当indexedDB内置方法无法满足数据查询需要时,可以利用游标遍历数据,挑选需要的数据出来;快速寻找位于objectStore末尾的记录时使用。
-
-
range(域):
-
用于挑选一定域内记录的工具。indexedDB内置了IDBKeyRange作为range内置工具对象.
-
一般我们在能使用key的地方都能使用range,用range代替key来进行查询。
-
在使用cursor时,可以通过range来前置遍历的范围。
var transaction = db.transaction(['fThings'], 'readonly') var objectStore = transaction.objectStore('fThings') objectStore.openCursor(keyRangeValue).onsuccess = e => { var cursor = e.target.result if (cursor) { // --- } } 复制代码
-
-
本质上
-
-
设计层面:transaction(事务)
- 什么是事务?
- 我们要做一件事情,有主体和客体。对于客体而言,它是一个什么状态?能不能满足我们现在要去做这件事的条件?如果它现在不能去做我们要做的事,该怎么办?
- 例如:我们要去ATM自动取款机取钱。对于ATM而言,它现在有没有钱,有没有人在排队,有没有机器坏掉?只有这些条件都允许我们取钱的时候,我们才能取到钱。
- 事务是一个状态机,它是一系列
事件请求(request)
的队列,并有权决定一系列事件是否可以操作成功(或放弃操作)。 - indexedDB的事务处理是内置的,自动的。
- 处于同一事务中的事件请求按进入队列的顺序依次执行,但对于数据的修改进行确定,却是在事务结束时,一旦中间有事件导致报错,中断事务,所有的事件操作都被废弃,没有一个修改是成功的。它对数据有保护性,防止写入操作造成已有数据坏掉。
- request:
- 当一个事务开启后,它具有一定的持续性。
- indexedDB的一切数据操作都在一个事务中进行。你可以在一个事务中做一件或多件事。要做一件事的时候,你需要在这个事务过程中,发起一个request.
- 一个request只做一件事。
- 事务是request的队列。
- indexedDB的任何一个操作都在一个仅包含自己的request中完成。
- 四种request:
- database request
- objectStore request
- index request
- cursor request
- 事务开启时,需要传入mode(只读,读写) 和 scope, 这个scope包含一个以上的objectStore.
- 事务中的数据操作,仅能对scope中的objectStore进行数据操作。
- request:
- 什么是事务?
SQLDB 与 IndexedDB 概念对比图:
IndexedDB 的 API 体系:
-
indexedDB
-
IDBDatabase
-
IDBTransaction
-
IDBRequest
-
IDBObjectStore
-
IDBIndex
-
IDBCursor
-
IDBKeyRange
参考: