代码改变世界

客户端持久化数据库---indexedDB使用

2019-07-12 21:54 龙恩0707 阅读(...) 评论(...) 编辑 收藏

阅读目录

一:什么是indexedDB数据库?

indexedDB是浏览器中的事务类型对象存储数据库。indexedDB适合大量的结构的数据存储。

适用场景:当数据量不是很大的时候,我们可以使用sessionStorage或LocalStorage来进行存储,但是当数据量非常大的时候,我们需要使用本地数据库来存储,因此这个时候 indexedDB 浏览器数据库应运而生。

indexedDB 有如下特点:

1)异步的;indexedDB打开数据库或获取数据的时候都是异步的,也就是说它不会堵塞浏览器操作。异步就是为了防止大量数据的读写操作,防止网页变慢或卡顿。

2)键值对存储。indexedDB内部采用对象仓库(object store) 来存放数据的,所有类型的数据都可以直接存储。在对象仓库中,
数据以 "键值对" 的形式保存,每一个数据记录都有对应的主键,主键是不可重复的。

3)支持事务型的。indexedDB执行的操作会按照事务来分组的,在一个事务中,要么所有的操作都成功,要么所有的操作都失败。

4)同源限制。indexedDB有同源限制,每一个数据库只能在自身域名下能访问,不能跨域名访问。

5)存储空间大。存储空间可以达到几百兆甚至更多。

6)支持二进制存储。它不仅可以存储字符串,而且还可以存储二进制数据(ArrayBuffer对象或Blob对象)。

2.1 打开或创建数据库

使用indexedDB的第一步是打开我们的数据库,使用 indexedDB.open()方法,如下所示:

<!DOCTYPE html>
<html>
<head>
  <title>indexedDB数据库使用</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0">
</head>
<body>
  <script type="text/javascript">
    var request = window.indexedDB.open('my-database', 1);
    console.log(request);
  </script>
</body>
</html>

打印 如上的 信息,如下所示:

该方法接收2个参数,第一个参数是字符串,表示数据库的名字。如果指定的数据库不存在的话,就会新建该数据库。第二个参数是一个整数,它表示的是数据库的版本。如果省略第二个参数的话,如果是新建数据库的话,默认为1. 如果是打开已有的数据库的话,默认就为当前的版本。

数据库被创建完成后,我们可以打开我们的控制台 -> application -> indexedDB 下 就有 我们刚刚的 my-database 数据库了,如下所示:

从上面打印的信息我们可以看到,创建一个数据库完成后,该实列有 onerror, onsuccess, onupgradeneeded 监听函数。及 result 属性值。

indexedDB.open() 方法返回的是一个IDBRequest对象,代表一个请求连接数据库的请求对象。该对象通过三种事件:onerror、onsuccess、onupgradeneeded 处理打开数据库的操作结果的。我们可以通过监听请求对象的 onsuccess 和 onerror 事件来定义连接成功或失败需要执行的方法。如下代码:

var request = window.indexedDB.open('my-database');
  var db;
  request.onerror = function() {
    console.log('数据库打开报错');
  }
  request.onsuccess = function(event) {
    console.log(event.target);
    db = request.result;
    console.log('数据库打开成功');
  }
  request.onupgradeneeded = function(event) {
    console.log('数据库升级事件');
    console.log(event.target);
    db = event.target.result;
  }

如上代码执行的结果如下:

如上我们可以看到,会执行我们的 onsuccess的方法,但是 onupgradeneeded 是不会执行的,该监听函数只监听当当前的版本号升级的时候才会被触发,也就是说,数据库的版本号更改为大于当前的数据库的版本才会被执行。

注意:从上面我们也可以看到 event.target === request 实列对象.

当我们现在把数据库的版本号升级到2的时候,之前的版本号是1,现在数据库的版本升级了,就会触发 onupgradeneeded 事件,如下代码所示:

var request = window.indexedDB.open('my-database', 2);
var db;
request.onupgradeneeded = function(event) {
  console.log('数据库升级事件');
  console.log(event.target);
  db = event.target.result;
}

实现效果如下所示:

2.2 创建对象仓库(或叫创建表)

 如上我们的indexedDB数据库新建完成后,我们现在要做的事情就行新建表格了。不过在indexedDB中没有数据库表,而叫对象仓库,不过它的作用就相当于一个数据库表。它使用的 createObjectStore 来创建的,如下代码演示:

var request = window.indexedDB.open('my-database', 3);
var db;
request.onupgradeneeded = function(event) {
  db = event.target.result;
  var store;
  if (!db.objectStoreNames.contains('Users')) {
    store = db.createObjectStore('Users', {keyPath: 'id', autoIncrement: true });
  }
  console.log('创建对象仓库成功');
  console.log(store);
}

如上代码,db.createObjectStore 方法接收2个参数,第一个为对象的仓库名(也可以理解我们之前的数据库表名),第二个参数为可选参数,值为一个js对象,该对象中的 keyPath属性为主键,autoIncrement 属性为 true,表示主键值自增。

db.objectStoreNames.contains('Users') 的含义:是否包含该对象仓库名(或叫表名)。如果不包含就创建一个。

2.3 创建索引

在indexedDB中,我们使用 createIndex 来创建它的索引。通过数据对象的某个属性来创建索引,在数据库中进行检索数据的时候,我们只能通过被设置为索引的属性来进行检索的。

如下代码:

var request = window.indexedDB.open('my-database', 4);
var db;
request.onupgradeneeded = function(event) {
  db = event.target.result;
  var store;
  if (!db.objectStoreNames.contains('newUsers')) {
    store = db.createObjectStore('newUsers', {keyPath: 'id', autoIncrement: true });
  }
  console.log(store);
  // 创建索引
  store.createIndex('userIndex', 'userName', { unique: false });
  store.createIndex('userEmail', 'email', { unique: true });
  console.log('创建索引成功');
}

如上代码:store.createIndex 方法接收三个参数,第一个为索引名,第二个为数据对象的属性,第三个参数是可选参数,值为一个js对象,该对象中的 unique 属性值为true,代表该索引值是唯一的,不可以相同的。如果为false的话,则可以相同。

索引创建完成后,我们需要事务操作了。

2.4 新增数据

在indexedDB中,我们能够使用事务来进行数据库的操作,事务有三个模式。

1. readOnly: 只读
2. readwrite: 可读可写
3. versionchange: 数据库版本变化

我们创建一个事务时,需要选择一种模式,如果不指定的话,则默认为只读模式。比如代码如下:

var transaction = db.transaction(['customers'], 'readwrite');

创建事务我们使用 db.transaction方法。该方法接收2个参数,第一个参数是字符串或数组,字符串是一个对象的仓库名。数组则是由对象仓库名组成的数组。第二个参数是事务模式。比如上面的模式是只读,或 可读可写。

新增数据指的是向对象仓库写入数据记录,这需要通过事务完成。该方法接收一个参数,保存到对象仓库中的对象。
如下代码所示:

var request = window.indexedDB.open('my-database', 4);
var db;
request.onupgradeneeded = function(event) {
  db = event.target.result;
  var store;
  if (!db.objectStoreNames.contains('newUsers')) {
    store = db.createObjectStore('newUsers', {keyPath: 'id', autoIncrement: true });
  }
  console.log(store);
  // 创建索引
  store.createIndex('userIndex', 'userName', { unique: false });
  store.createIndex('userEmail', 'email', { unique: true });
  console.log('创建索引成功');
}
request.onsuccess = function(event) {
  db = event.target.result;
  var transaction = db.transaction(['newUsers'], 'readwrite');
  var objStore = transaction.objectStore('newUsers');
  objStore.add({name: 'a', age: 10});
  objStore.add({name: 'b', age: 20});
}

如上代码,我们使用 var transaction = db.transaction(['newUsers'], 'readwrite'); 代码来创建一个事务,第一个参数 'newUsers' 是对象仓库名,是我们之前在 onupgradeneeded 函数里面创建仓库的。事务新建以后,我们使用 transaction.objectStore('newUsers') 这个方法,拿到 IDBObjectStore 对象,再通过表格对象的add()方法,向表格写入一条记录。如下所示:

2.5 读取数据

 读取数据也是通过事务完成的。如下代码:

var request = window.indexedDB.open('my-database', 4);
var db;
request.onsuccess = function(event) {
  db = event.target.result;
  var transaction = db.transaction(['newUsers'], 'readwrite');
  var objStore = transaction.objectStore('newUsers');
  // 读取数据
  var req = objStore.get(1);
  req.onsuccess = function(e) {
    if (req.result) {
      console.log('已经查询到数据为:');
      console.log(req.result);
    } else {
      console.log('未查询到数据');
    }
  }
}

如上面代码中,objStore.get()方法用于读取数据,参数是主键的值。比如我们来查看下我们的主键key在我们的Application下有如下key值;

如上我们获取的key = 1;因此打印的效果如下所示:

2.6 遍历数据

如果我们需要遍历整个存储空间的数据时,我们就需要使用流标,流标通过对象仓库的 openCursor方法创建打开,该方法可以传递2个参数,第一个参数是 IDBKeyRange对象,第二个参数表示流标的读取方向。
我们来看下第一个参数的使用方法如下所示:

/*
 boundRange 表示主键从1到10(包含1和10)的集合
 如果第三个参数是true的话,则表示不包含最小的键值1。
 如果第四个参数是true的话,则表示不包含最大键值10,默认都为false
*/
var boundRange = IDBKeyRange.bound(1, 10, false, false);

// onlyRange 表示由一个主键值的集合。only里面的参数值为主键值,整数类型。
var onlyRange = IDBKeyRange.only(1);

/*
 lowerRange 表示大于等于1的主键值的集合。
 第二个参数可选,为true表示不包含最小主键1,默认为false
*/
var lowerRange = IDBKeyRange.lowerBound(1, false);

/*
 upperRange 表示小于等于10的主键值的集合。
 第二个参数可选,为true则表示不包含最大主键10,false则包含,默认为false
*/
var upperRange = IDBKeyRange.upperBound(10, false);

openCursor方法 的第二个参数表示流标的读取方向,主要有以下几种:

next: 流标中的数据按主键值升序排序,主键值相等的数据都被读取到。
nextunique: 流标中的数据按主键值升序排序,主键值相等只读取第一条数据。
prev: 流标中的数据按主键值降序排序,主键值相等的数据都被读取。
prevunique: 流标中的数据按主键值降序排序,主键值相等只读取第一条数据。

如果不使用参数的话,则是查询所有的记录,使用方法代码如下所示:

var request = window.indexedDB.open('my-database', 4);
var db;
var datas = [];
request.onsuccess = function(event) {
  db = event.target.result;
  var transaction = db.transaction(['newUsers'], 'readwrite');
  var objStore = transaction.objectStore('newUsers');
  
  // 使用流标 openCursor
  objStore.openCursor().onsuccess = function(e) {
    var cursor = e.target.result;
    if (cursor) {
      console.log(cursor);
      datas.push(cursor);
      cursor.continue();
    } else {
      console.log('没有数据了');
      console.log(datas);
    }
  }
}

打印 cursor 信息如下所示:

下面我们使用 openCursor中两个参数来看下demo,如下所示代码:

var request = window.indexedDB.open('my-database', 6);
var db;
var datas = [];
request.onsuccess = function(event) {
  console.log(IDBKeyRange)
  console.log(111);
  db = event.target.result;
  var transaction = db.transaction(['newUsers'], 'readwrite');
  var objStore = transaction.objectStore('newUsers');
  var range = IDBKeyRange.bound(2, 10);
  // 使用流标 openCursor
  console.log(222);
  objStore.openCursor(range, 'next').onsuccess = function(e) {
    var cursor = e.target.result;
    if (cursor) {
      console.log(cursor);
      datas.push(cursor);
      cursor.continue();
    } else {
      console.log('没有数据了');
      console.log(datas);
    }
  }
}

如上 IDBKeyRange.bound(2, 10)方法的含义是:表示主键从2到10的集合. objStore.openCursor(range, 'next') 的含义是:流标中的数据按主键值升序排序,主键值相等的数据都被读取到。我们首先来看下我们的 my-database 数据库中 newUsers表中的数据如下:

可以看到如上面我们的id就是我们的主键。然后我们如上运行结果如下所示:

更多的相关IDBKeyRange的API请看官网

2.7 更新数据

 更新数据我们需要使用 IDBObject.put() 方法。该方法也是接收一个参数,为需要保存到对象仓库中的对象。

var request = window.indexedDB.open('my-database', 4);
var db;
request.onsuccess = function(event) {
  console.log(IDBKeyRange)
  console.log(111);
  db = event.target.result;
  var transaction = db.transaction(['newUsers'], 'readwrite');
  var objStore = transaction.objectStore('newUsers');

  var newValue = {
    'name': 'kongzhi',
    'age': '30',
    'id': 1
  };
  // 更新数据
  var req1 = objStore.put(newValue);

  // 获取主键为1的数据
  var req2 = objStore.get(1);

  // 加载主键为1的数据
  req2.onsuccess = function(e) {
    var cursor = e.target.result;
    if (cursor) {
      console.log(cursor);
    } else {
      console.log('没有数据了');
    }
  }
}

如上代码,我们使用 objStore.put(newValue); 这个方法去更新主键为1的数据,然后使用 objStore.get(1) 方法来获取主键为1的数据,最后监听 success 方法函数,成功后获取数据,打印信息如下图所示:

然后我们继续来看下 我们的控制台 -> application 下的 newUsers中的数据被更新成如下:

2.8 删除数据

我们使用 IDBObjectStore.delete() 方法用于删除记录。代码如下所示:

var request = window.indexedDB.open('my-database', 4);
var db;
request.onsuccess = function(event) {
  console.log(IDBKeyRange)
  console.log(111);
  db = event.target.result;
  var transaction = db.transaction(['newUsers'], 'readwrite');
  var objStore = transaction.objectStore('newUsers');

  // 删除主键为1的数据
  var res = objStore.delete(1);

  // 加载主键为1的数据
  res.onsuccess = function(e) {
    console.log('删除成功了');
  }
}

如上代码,我们使用 objStore.delete(1) 方法删除主键为1的代码,然后在控制台中我们看到会打印 删除成功的 文案,然后我们到 我们的 application 再看看如下所示:

如上我们可以看到,我们的主键为1的记录没有了。

2.9 使用索引

索引的作用就是可以让我们搜索任意字段,我们可以根据索引来搜索任何一条记录,有索引使我们搜索的速度更快,就好比我们的一本书目录一样,它也有
对应的索引含义,我们可以根据该索引能快速查找到我们这一本书上的某一个知识点在第几页。在这里我们默认是按照主键来搜索。

如上创建索引那块的地方,我们可以看到使用如下语法创建索引:

store.createIndex('userIndex', 'userName', { unique: false });

我们首先来看下我们 索引userIndex 有如下值:如下所示:

现在我们使用普通的流标的方法来获取索引,代码如下所示:

var request = window.indexedDB.open('my-database', 6);
var db;
var datas = [];
request.onupgradeneeded = function(event) {
  db = event.target.result;
  var store;
  if (!db.objectStoreNames.contains('newUsers2')) {
    store = db.createObjectStore('newUsers2', {keyPath: 'id', autoIncrement: true });
    console.log(store);
    // 创建索引
    store.createIndex('userIndex', 'userName', { unique: false });
    console.log('创建索引成功1111');
  }
}

request.onsuccess = function(event) {
  db = event.target.result;
  var transaction = db.transaction(['newUsers2'], 'readwrite');
  var objStore = transaction.objectStore('newUsers2');
  var req = objStore.index('userIndex').openCursor();
  req.onsuccess = function(e) {
    var res = e.target.result;
    if (res) {
      console.log(res);
      datas.push(res);
      res.continue();
    } else {
      console.log('没有数据了');
      console.log(datas);
    }
  }
}

然后我们在onsuccess函数内部使用 datas数组,往数组里面添加一条数据,然后使用 res.continue(); 继续调用,因此多条数据都会被添加到 datas 数组里面去了,如下所示:

2.10 清空所有的数据 clear() 方法

 我们现在来清空下 newUsers 仓库里面的数据,我们没有调用 clear() 方法清空之前,我们来看下 newUsers 表中有哪些数据,如下所示:

现在我们看如下代码,就可以清空 newUsers 仓库中所有的数据了,如下代码所示:

var request = window.indexedDB.open('my-database', 6);
var db;
var datas = [];
request.onsuccess = function(event) {
  console.log(IDBKeyRange)
  console.log(111);
  db = event.target.result;
  var transaction = db.transaction(['newUsers'], 'readwrite');
  var objStore = transaction.objectStore('newUsers');
  objStore.clear();
  objStore.onsuccess = function(e) {
    var res = e.target.result;
    console.log(res);
  }
}

然后我们继续来查看下我们的 Application 下的数据如下所示:

可以看到我们所有的数据都被清空了。