indexedDB 基本使用

indexedDB 简介:
indexedDB 是一种使用浏览器存储大量数据的方法.它创造的数据可以被查询,并且可以离线使用.
indexedDB

有以下特点:

  1. indexedDBWebSQL 数据库的取代品
  2. indexedDB 遵循同源协议(只能访问同域中存储的数据,而不能访问其他域的)
  3. API 包含 异步API同步API 两种:多数情况下使用 异步API ; 同步API 必须同 WebWorkers 一起使用, 目前没有浏览器支持 同步API
  4. indexedDB 是事务模式的数据库, 使用 key-value 键值对储存数据
  5. indexedDB 不使用结构化查询语言( SQL ). 它通过索引( index )所产生的指针( cursor )来完成查询操作

一、使用indexedDB的基本模式

  1. 打开数据库并且开始一个事务。
  2. 创建一个 objecStore
  3. 构建一个请求来执行一些数据库操作,像增加或提取数据等。
  4. 通过监听正确类型的 DOM 事件以等待操作完成。
  5. 在操作结果上进行一些操作(可以在 request 对象中找到)

二、创建、打开数据库

indexedDB 存在于全局对象 window 上, 它最重要的一个方法就是 open 方法, 该方法接收两个参数:

  • dbName // 数据库名称 [string]
  • version // 数据库版本 [整型number]
var DB_NAME = 'indexedDB-test', VERSION = 1, db;
var request = indexedDB.open(DB_NAME, VERSION);
request.onsuccess = function(event) {
    db = event.target.result;
    // console.log(event.target === request); // true
    db.onsuccess = function(event) {
        console.log('数据库操作成功!');
    };
    db.onerror = function(event) {
        console.error('数据库操作发生错误!', event.target.errorCode);
    };
    console.log('打开数据库成功!');
};
request.onerror = function(event) {
    console.error('创建数据库出错');
    console.error('error code:', event.target.errorCode);
};
request.onupgradeneeded = function(event) { 
   // 更新对象存储空间和索引 .... 
};

若是本域下不存在名为 DB_NAME 的数据库,则上述代码会创建一个名为 DB_NAME 、版本号为 VERSION 的数据库; 触发的事件依次为: upgradeneededsuccess .

若是已存在名为 DB_NAME 的数据库, 则上述代码会打开该数据库; 只触发 success / error 事件,不会触发 upgradeneeded 事件. db 是对该数据库的引用.

三、创建对象存储空间和索引

在关系型数据库(如mysql)中,一个数据库中会有多张表,每张表有各自的主键、索引等;

key-value 型数据库(如indexedDB)中, 一个数据库会有多个对象存储空间,每个存储空间有自己的主键、索引等;

创建对象存储空间的操作一般放在创建数据库成功回调里:

request.onupgradeneeded = function(event) { // 更新对象存储空间和索引 .... 
    var database = event.target.result;
    var objectStore = database.createObjectStore("movies", { keyPath: "id" });
    objectStore.createIndex('alt', 'alt', { unique: true });
    objectStore.createIndex('title', 'title', { unique: false });
};

onupgradeneeded 是我们唯一可以修改数据库结构的地方。在这里面,我们可以创建和删除对象存储空间以及构建和删除索引。

在数据库对象 database 上,有以下方法可供调用:

  1. createObjectStore(storeName, configObj) 创建一个对象存储空间
    • storeName // 对象存储空间的名称 [string]
    • configObj // 该对象存储空间的配置 [object] (其中的keyPath属性值,标志对象的该属性值唯一)
  2. createIndex(indexName, objAttr, configObj) 创建一个索引
    • indexName // 索引名称 [string]
    • objAttr // 对象的属性名 [string]
    • configObj // 该索引的配置对象 [object]

四、增加和删除数据

对数据库的操作(增删查改等)都需要通过 事务 来完成, 事务 具有三种模式:

  • readonly 只读(可以并发进行,优先使用)
  • readwrite 读写
  • versionchange 版本变更

向数据库中增加数据

前面提到,增加数据需要通过 事务事务 的使用方式如下:

var transaction = db.transaction(['movies'], 'readwrite');
transaction.oncomplete = function(event) {
    console.log('事务完成!');
};
transaction.onerror = function(event) {
    console.log('事务失败!', event.target.errorCode);
};
transaction.onabort = function(event) {
    console.log('事务回滚!');
};

数据库对象的 transaction() 方法接收两个参数:

  • storeNames // 对象存储空间,可以是对象存储空间名称的数组,也可以是单个对象存储空间名称,必传 [array|string]
  • mode // 事务模式,上面提到的三种之一,可选,默认值是 readonly [string]

这样,我们得到一个事务对象 transaction , 有三种事件可能会被触发: complete , error , abort . 现在,我们通过事务向数据库 indexedDB-test 的 对象存储空间 movies 中插入数据:

var objectStore = transaction.objectStore('movies');  // 指定对象存储空间
var data = [{
  "title": "寻梦环游记",
  "year": "2017",
  "alt": "https://movie.douban.com/subject/20495023/",
  "id": "20495023"
}, {
  "title": "你在哪",
  "year": "2016",
  "alt": "https://movie.douban.com/subject/26639033/",
  "id": "26639033"
}, {
  "title": "笔仙咒怨",
  "year": "2017",
  "alt": "https://movie.douban.com/subject/27054612/",
  "id": "27054612"
}];
data.forEach(function(item, index){
    var request = objectStore.add(item);
    request.onsuccess = function(event) {
        console.log('插入成功!', index);
        console.log(event.target.result, item.id); // add()方法调用成功后result是被添加的值的键(id)
    };
});

通过事务对象 transaction ,在 objectStore() 方法中指定对象存储空间,就得到了可以对该对象存储空间进行操作的对象 objectStore .

向数据库中增加数据, add() 方法增加的对象,若是数据库中已存在相同的主键,或者唯一性索引的键值重复,则该条数据不会插入进去;

增加数据还有一个方法: put() , 使用方法和 add() 不同之处在于,数据库中若存在相同主键或者唯一性索引重复,则会更新该条数据,否则插入新数据。

从数据库中删除数据

删除数据使用 delete 方法,同上类似:

var request = 
    db.transaction(['movies'], 'readwrite')
      .objectStore('movies')
      .delete('27054612');  // 通过键id来删除
request.onsuccess = function(event) {
    console.log('删除成功!');
    console.log(event.target.result);
};

从数据中获取数据

获取数据使用 get 方法,同上类似:

var request = 
    db.transaction('movies')
       .objectStore('movies')
       .get('9999682');  // 通过键alt来获取
request.onsuccess = function(event) {
    console.log('获取成功!', event.target.result);
};

五、使用索引

在前面,我们创建了两个索引 alttitle , 配置对象里面的 unique 属性标志该值是否唯一

现在我们想找到 alt 属性值为 https://movie.douban.com/subject/26639033/ 的对象,就可以使用索引。

var alt = 'https://movie.douban.com/subject/26639033/';
var objectStore = db.transaction('movies').objectStore('movies');  // 打开对象存储空间
var index = objectStore.index('alt');  // 使用索引'alt'
var request = index.get(alt);          // 创建一个查找数据的请求
request.onsuccess = function(event) {
    console.log('The result is:', event.target.result);
};
var noDataTest = index.get('testalt');  // 没有该对象时的测试
noDataTest.onsuccess = function(event) {
    console.log('success! result:', event.target.result);
};
noDataTest.onerror = function(event) {
    console.log('error! event:', event);
};

使用唯一性索引,我们可以得到唯一的一条数据(或者 undefined ),那么使用非唯一性索引呢?

我们向数据库中插入一条数据,使 title 重复:

db.transaction('movies', 'readwrite').objectStore('movies')
.add({ alt: 'https://movie.douban.com/subject/27054612121/',
    title: '寻梦环游记',
    year: '2017',
    id: '123456789'
})
.onsuccess = function(event) { console.log('插入成功!'); };

使用索引 title 获取 title 值为 寻梦环游记 的对象:

var indexName = 'title', title = '寻梦环游记';
var objectStore = db.transaction('movies').objectStore('movies');
var index = objectStore.index(indexName);  // 使用索引'alt'
var request = index.get(title);          // 创建一个查找数据的请求
request.onsuccess = function(event) {
    console.log('The result is:', event.target.result);
};

我们得到的是 键值 最小的那个对象.

使用一次索引,我们只能得到一条数据; 如果我们需要得到所有 title 属性值为 寻梦环游记 的对象,我们可以使用 游标 .

六、使用游标

得到一个可以操作游标的请求对象有两个方法:

  • openCursor(keyRange, direction)
  • openKeyCursor(keyRange, direction)
    这两个方法接收的参数一样, 两个参数都是可选的: 第一个参数是限制值得范围,第二个参数是指定游标方向

游标的使用有以下几处:

  • 在对象存储空间上使用: var cursor = objectStore.openCursor()
  • 在索引对象上使用: var cursor = index.openCursor()

在对象存储空间上使用游标

使用游标常见的一种模式是获取对象存储空间上的所有数据.

var list = [];
var objectStore = db.transaction('movies').objectStore('movies');
objectStore.openCursor().onsuccess = function(event) {
    var cursor = event.target.result;
    if (cursor) {
        console.log('cursor:', cursor);
        list.push(cursor.value);
        cursor.continue();
    } else {
        console.log('Get all data:', list);
    }
};

使用游标时,需要在成功回调里拿到 result 对象,判断是否取完了数据:若数据已取完, resultundefined ; 若未取完,则 result 是个 IDBCursorWithValue 对象,需调用 continue() 方法继续取数据。 也可以根据自己需求, 对数据进行过滤。

indexedDB2 规范中,在对象存储空间对象上纳入了一个 getAll() 方法,可以获取所有对象:

objectStore.getAll().onsuccess = function(event) {
    console.log('result:', event.target.result);
};

在索引上使用游标

接着本文上述使用索引的例子,在索引 title 上使用 openCursor() 方法时,若不传参数,则会遍历所有数据,在成功回调中的到的 result 对象有以下属性:

  • key 数据库中这条对象的 title 属性值
  • primaryKey 数据库中这条对象的 alt
  • value 数据库中这条对象
  • direction openCursor()方法传入的第二个对象,默认值为 next
  • source IDBIndex对象 举例如下:
    var index = db
    .transaction('movies')
    .objectStore('movies').index('title');
    index.openCursor().onsuccess = function(event) {
      var cursor = event.target.result;
      if (cursor) {
          console.log('cursor:', cursor);
          cursor.continue();
      }
    };
    

在索引 title 上使用 openKeyCursor() 方法,若不传参数,同样也会遍历所有数据, result 对象属性如下:

  • key 数据库中这条对象的 title 属性值
  • primaryKey 数据库中这条对象的 alt
  • direction openCursor()方法传入的第二个对象,默认值为 next
  • source altBIndex对象

openCursor() 方法相比,得到的数据少一个 value 属性,是没有办法得到存储对象的其余部分

前面说到,我们要根据索引 title 获取所有 title 属性值为 寻梦环游记 的对象,要使用游标,而又不想遍历所有数据,这时就要用到 openCursor() 的第一个参数: keyRange

keyRange 是限定游标遍历的数据范围,通过 IDBKeyRange 的一些方法设置该值:

var singleKeyRange = IDBKeyRange.only("寻梦环游记"), list = [];
var index = db
.transaction('movies')
.objectStore('movies').index('title');
index.openCursor(singleKeyRange).onsuccess = function(event) {
 var cursor = event.target.result;
 if (cursor) {
 console.log('cursor.value:', cursor.value);
 list.push(cursor.value);
 cursor.continue();
 } else {
     console.log('list:', list);
 }
};

IDBKeyRange 其他一些方法:

// 匹配所有在 "Bill" 前面的, 包括 "Bill"
var lowerBoundKeyRange = IDBKeyRange.lowerBound("Bill");
 
// 匹配所有在 “Bill” 前面的, 但是不需要包括 "Bill"
var lowerBoundOpenKeyRange = IDBKeyRange.lowerBound("Bill", true);
 
// 匹配所有在'Donna'后面的, 但是不包括"Donna"
var upperBoundOpenKeyRange = IDBKeyRange.upperBound("Donna", true);
 
// 匹配所有在"Bill" 和 "Donna" 之间的, 但是不包括 "Donna"
var boundKeyRange = IDBKeyRange.bound("Bill", "Donna", false, true);

更多请参考 MDN|IDBKeyRange

游标默认遍历方向是按主键从小到大,有时候我们倒序遍历,此时可以给 openCursor() 方法传递第二个参数: direction : next | nextunique | prev | prevunique

var singleKeyRange = IDBKeyRange.only("寻梦环游记"), list = [];
var index = db
.transaction('movies')
.objectStore('movies').index('title');
index.openCursor(singleKeyRange, 'prev').onsuccess = function(event) {
 var cursor = event.target.result;
 if (cursor) {
 console.log('cursor.value:', cursor.value);
 list.push(cursor.value);
 cursor.continue();
 } else {
     console.log('list:', list);
 }
};

传了 prev 的结果是按倒序遍历的.

因为 “name” 索引不是唯一的,那就有可能存在具有相同 name 的多条记录。 要注意的是这种情况不可能发生在对象存储空间上,因为键必须永远是唯一的。 如果你想要在游标在索引迭代过程中过滤出重复的,你可以传递 nextunique (或 prevunique , 如果你正在向后寻找)作为方向参数。 当 nextunique 或是 prevunique 被使用时,被返回的那个总是键最小的记录。

var singleKeyRange = IDBKeyRange.only("寻梦环游记"), list = [];
var index = db
.transaction('movies')
.objectStore('movies').index('title');
index.openCursor(singleKeyRange, 'prevunique').onsuccess = function(event) {
 var cursor = event.target.result;
 if (cursor) {
 console.log('cursor.value:', cursor.value);
 list.push(cursor.value);
 cursor.continue();
 } else {
     console.log('list:', list);
 }
};

七、关闭和删除数据库

  • 关闭数据库只需要在数据库对象 db 上调用 close() 方法即可

    db.close();
    

    关闭数据库后, db 对象仍然保存着该数据库的相关信息,只是无法再开启事务(调用开启事务方法会报错,提示数据库连接已断开):

  • 删除数据库则需要使用 indexedDB.deleteDatabase(dbName) 方法

    window.indexedDB.deleteDatabase(dbName);
    

八、indexedDB的局限性

以下情况不适合使用IndexedDB

  • 全球多种语言混合存储。国际化支持不好。需要自己处理。
  • 和服务器端数据库同步。你得自己写同步代码。
  • 全文搜索。

注意,在以下情况下,数据库可能被清除:

  • 用户请求清除数据。
  • 浏览器处于隐私模式。最后退出浏览器的时候,数据会被清除。
  • 硬盘等存储设备的容量到限。
  • 不正确的
  • 不完整的改变.

总结

  1. 使用 indexedDB.open(dbName, version) 打开一个数据库连接
  2. 使用 indexedDB.deleteDatabase(dbName) 删除一个数据库
  3. 在数据库对象 db 上使用 createObjectStore(storeName, config) 创建对象存储空间
  4. 在对象存储空间 objectStore 上使用 createIndex(indexName, keyName, config) 创建索引
  5. 对数据库的操作都需要通过 事务 完成: var transction = db.transaction([storeName], mode)
  6. 数据库的增删改查均通过 objectStore 对象完成, var objectStore = transaction.objectStore(storeName)
  7. 对数据库数据操作有: add()get()delete()put 等方法
  8. 查找数据可以使用 索引 : objectStore.index(indexName)
  9. 遍历和过滤数据可以使用 游标 : openCursor(keyRange, direction)

参考链接

WEB前端-伯乐在线责编内容来自:WEB前端-伯乐在线 (源链) | 更多关于

阅读提示:酷辣虫无法对本内容的真实性提供任何保证,请自行验证并承担相关的风险与后果!
本站遵循[CC BY-NC-SA 4.0]。如您有版权、意见投诉等问题,请通过eMail联系我们处理。
酷辣虫 » 综合编程 » indexedDB 基本使用

喜欢 (0)or分享给?

专业 x 专注 x 聚合 x 分享 CC BY-NC-SA 4.0

使用声明 | 英豪名录