IndexedDB数据库

介绍

IndexedDB就是一个数据库
其最大的特点是: 使用对象保存数据,而不是使用表来保存数据,同时,它是异步的

使用方式

连接数据库

要使用它必须先打开,通过 indexDB.open(name, version)方法打开一个数据库

  • name : 表示数据要打开的数据库的名称
  • version:为打开数据库的版本号

indexDB.open()方法的原理

分为两种情况:
1. 传入的数据库不存在
当传入的数据库不存在时,该方法就会创建一个名为name的数据库,并打开它,此时,会先触发upgradeneeded事件;调用该函数会返回一个IDBRequest对象,可以在该对象上添加onsuccess事件onerror事件
注意:当打开一个不存在的数据库时会触发upgradeneeded事件,这是触发该事件的一种途径,为什么会触发该事件呢?该事件有什么作用?留个疑问在这儿,等会解答。

2. 传入的数据库存在
这里分为两种情况:

  • 当传入的数据库存在,且version版本号与将要打开的数据库版本号也相同
    则直接打开该数据库,如果成功,则会触发onsuccess事件,失败则触发onerror事件
    注意:这里并不会触发upgradeneeded事件,为什么?留个疑问

  • 当传入的数据库存在,但是传入的version版本号高于将要打开的数据库的版本号
    则直接打开该数据库,同时触发upgradeneeded事件,然后再触发onsuccess事件onerror事件,这里也触发了onupdateneeded事件

upgradeneeded事件

触发该事件的条件:当打开的数据库不存在,或者传入的数据库版本version高于当前版本,则会触发该事件

upgradeneeded事件的作用:当打开了一个数据库之后,需要开辟一个名为:对象存储空间 的玩意(可以理解为数据就是存放在这个空间里面,一个数据库可以创建多个对象存储空间),而 对象存储空间 只能在upgradeneeded事件的处理函数中创建

使用时,注意以下两种情况:

  1. 当我们第一次打开创建数据库时,会触发upgradeneeded事件,我们就需要在其中创建对象存储空间

  2. 当我们对数据库版本进行更新时,也会触发该事件,这时可以在此创建新的对象存储空间,原来的对象存储空间仍然存在

注意:如果需要对对象存储空间进行修改,那么只能先将存储在它里面的数据读取出来,再将其删除,然后使用新的选项去创建它,再写入原来的数据

打开数据库并创建对象存储空间的代码:

// 对于该API,各浏览器还未同一,所以需要对一些接口添加前缀
window.indexedDB = window.indexedDB || window.msIndexedDB || window.mozIndexedDB || window.webkitIndexedDB;
window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction || {READ_WRITE: "readwrite"};
window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;
window.IDBCursor = window.IDBCursor || window.webkitIDBTransaction;

// 判断浏览器是否支持IndexedDB
if (!window.indexedDB) {
window.alert("Your browser doesn't support a stable version of IndexedDB.")
}

var request , db;
// 打开或创建 名为dbName的数据库
request = window.indexedDB.open('dbName', 2)
request.onsuccess = function (event) {
   db = event.target.result;
}

request.onerror = function (event) {
   console.log('错误代码: ' + event.target.errorCode);
}

request.onupgradeneeded = function(event) {
  db = event.target.result;  // 
  // 创建一个   对象存储空间,名为customers
  var objectStore = db.createObjectStore('customers', {keyPath: 'ssn'});
  // 对于某些数据,可以为一个对象存储空间指定多个键。比如,若要通过用户ID 和用户名 两种方式来保存用户资料,就需要通过两个键来存取记录
  // 因此可以使用createIndex,名字是有可能重复的,所以其unique 设置为 false ;第一个name是索引的名字,该名字是索引的名字,第二个name是索引的属性的名字,该名字要与对象中的属性相同
  objectStore.createIndex('name', 'name', { unique: false});

  // 创建一个email的索引,该email是独特的,所以 unique 设置为 true
  objectStore.createIndex('email', 'email', { unique: true});
}

存储数据

存储数据有两种方法:add()方法put()方法

这两种方法的区别主要体现在:当要添加数据的对象存储空间中已经存在有相同键的数据时,使用add()方法添加数据会报错误,而put()方法则会对现有数据进行更新,所以add()方法一般用于初始化数据,而put()方法用于更新数据

代码如下:

// customerData 为要存储的数据
const customerData = [{ ssn: '444-44-4444', name: 'AAA', age: 35, email: '[AAA@company.com](mailto:AAA@company.com)'},{ ssn: '666-66-6666', name: 'CCC', age: 35, email: '[CCC@company.com](mailto:CCC@company.com)'},{ ssn: '777-77-7777', name: 'DDD', age: 32, email: '[DDD@home.org](mailto:DDD@home.org)'},{ ssn: '555-55-5555', name: 'BBB', age: 32, email: '[BBB@home.org](mailto:BBB@home.org)'},
];

// 创建一个事务,该事务将要对名为“customers”的对象存储空间进行 read和write 操作,并返回事务索引
let transaction = db.transaction('customers', 'readwrite'); 

// 取得索引后,使用objectStore()方法并传入存储空间的名称,就可以访问特定的存储空间,这两步是必须的
let store = transaction.objectStore('customers'); 

// 添加数据到数据库中
for (var i in customerData) {
  // 返回的req也是一个对象,可以为其添加onsuccess和onerror事件,来检测数据是否添加成功
  let req = store.put(customerData[i]);   // 往一个存储空间中添加数据
  
}
// 判断事务整个操作完成
transaction.oncomplete = function(event) {
  console.log(event.target);
  alert('存储数据完成');
};
}

如上就将数据存储到数据库dbNames的customers对象存储空间中

上面代码中提到了 [事务],这里先记住:凡是涉及到对数据库的读写删除操作,都需要通过 [事务] 来完成

事务和查询操作数据

最简单的创建事务的方式是:
var transaction = db.transaction(); // db就是前面的数据库对象
这种方式创建的事务,只能读取数据库中保存的所有对象

一般用法是:
var transaction = db.transaction('customes', 'readwrite');
表示只加载customers对象存储空间中的数据,并且是以可读可写的方式加载

如果不传第二个参数,则表示只可访问,不可修改;

这里返回的transaction是事务的索引

然后使用objectStore()方法并传入对象存储空间的名称,就可以访问特定的存储空间了;

如下:

let transaction = db.transaction('customers', 'readwrite'); 
let store = transaction.objectStore('customers'); 

取得了上面的store后,我们可以使用如下方法对数据进行操作:

  • add()和put()方法:用于存储数据
    let req = store.add(data);
  • get(key)方法:获取键为key的对象
    let req = store.get(key);
  • delete(key)方法:删除键为key的对象
    let req = store.delete(key);
  • clear()方法:清空对象存储空间中的所有对象
    let req = store.clear();
    使用上述方法会返回一个对象,通过对其添加onsuccess和onerror事件,可以检测操作是否成功

注意:通过oncomplete事件对象,访问不到get()请求返回的任何数据,必须在响应请求的onsuccess事件处理程序中才能访问到数据

使用游标查询数据

使用事务可以直接通过 已知的键检索单个对象。而在需要检索多个对象时,则需要在事务内创建游标。

游标并不会提前收集结果,游标先指向结果中的第一项,在接到查找下一项的指令时,才会指向下一项

如下:

let transaction = db.transaction('customers', 'readwrite'),
let store = transaction.objectStore('customers'),
let request = store.openCursor(null) ; // 这里创建游标
request.onsuccess = function (event) {
  // event.target.result 中保存的是在存储空间中查询到的对象
  // event.target.result 中有几个属性值,可以了解到查询到的对象中的细节,
  // key: 当前访问的对象的键
  // value:当前访问的实际对象
  // primaryKey: 游标使用的键
  // direction:数值,表示游标移动的方向
  let cursor = event.target.result;
  let value, updateRequest, deleteRequest;

  // 这里必须要检查游标中是否有数据
  if (cursor) {
    if (cursor.key === '555-55-5555') {
      value = cursor.value;   // 获取到实际的访问对象
      value.name = 'hexon';   // 修改对象的name属性
      // 调用update()方法可以用指定的对象,更新对象的value
      updateRequest = cursor.update(value);     
      updateRequest.onsuccess = function() {
          // 处理成功
       }
    }
    cursor.continue() ;  // 移动到下一项,会触发下一次请求,同时成功则触发request.onsuccess
  }
}

上面例子中,可以使用cursor.delete()方法删除当前项

键范围

游标也可以接受一个键,也就是通过键来设定游标查找的范围;
代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>WebStorage DEMO</title>
</head>
<body>
<div class="networkStatus">
<button class="clear">清空数据</button>
<button class="add">添加数据</button>
<button class="query">查询数据</button>
<button class="delete">删除数据</button>
<button class="cursor">使用游标查询</button>
<button class="keyRange">使用keyrange查询</button>
<button class="index">使用index</button>
</div>

<script>
let network = document.querySelector('.networkStatus'),
addBtn = document.querySelector('.add'),
queryBtn = document.querySelector('.query'),
deleteBtn = document.querySelector('.delete'),
cursorBtn = document.querySelector('.cursor'),
clearBtn = document.querySelector('.clear'),
keyRange = document.querySelector('.keyRange'),
indexBtn = document.querySelector('.index')
;

// 判断网路是否在线
// if (navigator.onLine) {
// network.innerText = "网络在线";
// } else {
// network.innerText = "网络掉线";
// }

// // 监控网络状态的事件:online 和 offline, 这两个事件在window对象上
// window.addEventListener('online', () => {
// network.innerText = "网络在线";
// });

// window.addEventListener('offline', () => {
// network.innerText = "网络掉线";
// });

//--------cookie的使用---------------
let CookieUtil = {
get: (name) => {
let cookieName = encodeURIComponent(name) + "=",
cookieStart = document.cookie.indexOf(cookieName),
cookieValue = null;

  if (cookieStart > -1) {
    let cookieEnd = document.cookie.indexOf(';', cookieStart);
    if (cookieEnd === -1) {
      cookieEnd = document.cookie.length;
    }
    cookieValue = decodeURIComponent(document.cookie.substring(cookieStart + cookieName.length, cookieEnd));
  }

  return cookieValue;
},
set: function (name, value, expires, path, domain, secure) {
  let cookieText = encodeURIComponent(name) + '=' +
                   encodeURIComponent(value);

  if (expires instanceof Date) {
    cookieText += '; expires=' + expires.toGMTString();
  }

  if (path) {
    cookieText += '; path=' + path;
  }

  if (domain) {
    cookieText += '; domain=' + domain;
  }

  if (secure) {
    cookieText += '; secure';
  }

  document.cookie = cookieText;
},

// 删除cookie, 并没有直接的删除cookie的方法,这里通过重新设置cookie名称,来对cookie进行替换
// 同时 将过期时间expires设置为过去的时间,
unset: function(name, path, domain, secure) {
  this.set(name, '', new Date(0), path, domain, secure);
}

}

CookieUtil.set('name', 'hexon');
CookieUtil.set('book', 'Profession Javascript');

// 读取cookie的值
// console.log(CookieUtil.get('name'));
// console.log(CookieUtil.get('book'));

// 删除cookie
CookieUtil.unset('name');
CookieUtil.unset('book');

// 设置cookie, 包括它的路径、域、失效日期
CookieUtil.set('name', 'Hexon', 'books/projs/', 'www.wrox.com', new Date('January 1, 2017'));

// 删除刚刚设置的cookie
CookieUtil.unset('name', 'books/projs/', 'www.www.wrox.com');

// 设置安全的cookie
CookieUtil.unset('name', 'hexon', null, null, null, null, true)

// --- IndexedDB 数据库的使用
var request = window.indexedDB.open('dbName', 2)
var db;
const dbName = 'the_name';
// 创建一个数据
const customerData = [
{ ssn: '444-44-4444', name: 'AAA', age: 35, email: 'AAA@company.com'},
{ ssn: '666-66-6666', name: 'CCC', age: 35, email: 'CCC@company.com'},
{ ssn: '777-77-7777', name: 'DDD', age: 32, email: 'DDD@home.org'},
{ ssn: '555-55-5555', name: 'BBB', age: 32, email: 'BBB@home.org'},

];

window.indexedDB = window.indexedDB || window.msIndexedDB || window.mozIndexedDB || window.webkitIndexedDB;
window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction || {READ_WRITE: "readwrite"};
window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;
window.IDBCursor = window.IDBCursor || window.webkitIDBTransaction;

if (!window.indexedDB) {
window.alert("Your browser doesn't support a stable version of IndexedDB.")
}

// 3 是建立的数据库版本,如果名为MyTestDatabase的数据库不存在,就会创造该数据库,然后 onupgradeneeded 事件会被触发;
// 如果数据库存在,但是对版本升级了,也会触发onupgradeneeded事件,
// 注意:版本号是一个 unsigned long long 类型的值,因此不要使用float,否则会将其转换为其最接近的整数

// 生成处理程序
request.onerror = function (event) {
// do Something
alert('Database error: ' + event.target.errorCode);
};

request.onsuccess = function (event) {
// do Something
console.log('创建数据库成功');
db = event.target.result; // 创建成功后,e.target.result 中存储的是IDBDatabase对象的实例
}

// 当创建一个新的数据库 或者 更新已存在数据库的版本, onupgradeneeded事件将会被触发,新的对象存储在event.target.result中。
// 在该处理程序中,数据库已经具有先前版本的对象存储,因此不必再次创建这些对象存储,只需要创建任何我们需要的对象存储,或者
// 从先前版本中删除不在需要的对象存储。如果需要更改当前对象存储,则必须先删除旧的对象存储,然后在使用新的选项创建。
// 删除旧的对象存储,在其上的信息都会被删除;
// 注意:该事件是唯一一个能够对数据库进行操作的地方,在该事件里面,你对对象存储进行删除、修改或移除索引
request.onupgradeneeded = function(event) {
console.log('onupgradeneeded');
var db = event.target.result;

// 创建一个   对象存储空间,名为customers
var objectStore = db.createObjectStore('customers', {keyPath: 'ssn'});
// 对于某些数据,可以为一个对象存储空间指定多个键。比如,若要通过用户ID 和用户名 两种方式来保存用户资料,就需要通过两个键来存取记录
// 因此可以使用createIndex,名字是有可能重复的,所以其unique 设置为 false ;第一个name是索引的名字,该名字是索引的名字,第二个name是索引的属性的名字,该名字要与对象中的属性相同
objectStore.createIndex('name', 'name', { unique: false});

// // 创建一个email的索引,该email是独特的,所以 unique 设置为 true
objectStore.createIndex('email', 'email', { unique: true});

}

function save(data) {
/// 对于数据库的对象存储空间中数据的读取或修改数据,都要通过事物来组织所有操作
// 最简单的创建事物的方法是:var transaction = db.transaction();
let transaction = db.transaction('customers', 'readwrite'); // 创建一个事务,并定义该事务的操作为 “readwrite” ,并返回其索引
let store = transaction.objectStore('customers'); // 取得索引后,使用objectStore()方法并传入存储空间的名称,就可以访问特定的存储空间

for (var i in customerData) {
  let req = store.put(customerData[i]);   // 往一个存储空间中添加数据
}

transaction.oncomplete = function(event) {
  console.log(event.target);
  alert('存储数据完成');
};

transaction.onsuccess = function(event ) {
  console.log('onsuccess 事件');
}

}

function clear() {
// body...
let transaction = db.transaction('customers', 'readwrite');
let store = transaction.objectStore('customers').clear();
store.onerror = function(event) {
console.log('清空数据失败');
}
store.onsuccess = function(event) {
console.log('清空数据成功');
}
}

// 使用事务 直接通过已知的键索引 单个对象 (只能索引单个对象)
function getData() {
let transaction = db.transaction('customers', 'readwrite'); // 创建一个事物, 并定义该事务的操作为 "readonly"
let store = transaction.objectStore('customers').get('444-44-4444'); // 使用get() 可以取得值

store.onerror = function (event) {
  alert('did not get the object');
}

store.onsuccess = function (event) {
  var result = event.target.result;
  console.log(result);
  alert('获取数据完成! 年龄是: ' + result.age);
}

}

function deleteData() {
let transaction = db.transaction('customers', 'readwrite');
let store = transaction.objectStore('customers');
store.delete('444-44-4444');
alert('s删除数据完成');
}

// 在事务内创建游标查询 可以索引 多个对象(注意: 是多个对象)
// 游标不提前手机结果
function cursorQuery() {
let transaction = db.transaction('customers', 'readwrite'),
store = transaction.objectStore('customers'),
request = store.openCursor(null) ; // 这里创建游标

request.onsuccess = function (event) {

  // event.target.result 中保存的是在存储空间中查询到的对象
  // event.target.result 中有几个属性值,可以了解到查询到的对象中的细节,
  // key: 当前访问的对象的键
  // value:当前访问的实际对象
  // primaryKey: 游标使用的键
  // direction:数值,表示游标移动的方向

  let cursor = event.target.result;
  let value, updateRequest, deleteRequest;
  if (cursor) {
  //   if (cursor.key === '555-55-5555') {
  //     value = cursor.value;   // 获取到实际的访问对象
  //     value.name = 'hexon';   // 修改对象的name属性

  //     updateRequest = cursor.update(value);      // 调用update()方法可以用指定的对象,更新对象的value
  //     updateRequest.onsuccess = function() {
  //       // 处理成功
  //     }
  //     updateRequest.onerror = function() {
  //       // 处理失败
  //     }


  //     // 使用游标删除当前项
  //     // deleteRequest = cursor.delete();
  //     // deleteRequest.onsuccess = function() {
  //     //   // 删除成功处理
  //     // }
  //     // deleteRequest.onerror = function() {
  //     //   // 删除失败处理
  //     // }


  //   }
  //   console.log(event.target.result);
  // }
  console.log(cursor.value);
  cursor.continue();      // 移动到下一项,
  }
  request.onerror = function(event) {
    console.log('游标查询创建失败')
  }
}

}

// 使用keyrange查询
function keyRangeQuery() {
let transaction = db.transaction('customers', 'readwrite')
let store = transaction.objectStore('customers');
// 使用bound()方法 定义键范围
let range = IDBKeyRange.bound('555-55-5555', '777-77-7777', true, false);
// 将键传入游标创建
let request = store.openCursor(range);

request.onsuccess = function(event) {
  let cursor = event.target.result;
  if (cursor) {
    console.log('游标查询到的值' + JSON.stringify(cursor.value));
    cursor.continue()     // 移动到下一项
  }

}

request.onerror = function(event) {
  console.log("使用游标 + keyrange 查询失败")
}

}

// 使用索引
function useIndex() {
let store = db.transaction('customers').objectStore('customers'),
index = store.index('name');
request = index.openCursor();
request.onsuccess = function (event) {
let cursor = event.target.result;
if (cursor) {
console.log(cursor);
cursor.continue();
}
}
}

addBtn.addEventListener('click', function(e) {
save();
}, false);

deleteBtn.addEventListener('click', function(e) {
deleteData();
}, false);

queryBtn.addEventListener('click', function(e) {
getData();
}, false);

cursorBtn.addEventListener('click', function(e) {
cursorQuery();
}, false);

clearBtn.addEventListener('click', function(e) {
clear();
}, false);

keyRange.addEventListener('click', function(e) {
keyRangeQuery();
}),

indexBtn.addEventListener('click', function(e) {
useIndex();
})

</script>

</body>
</html>

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容

  • 前言 在 HTML5 的本地存储中,有一种叫 indexedDB 的数据库,该数据库是一种存储在客户端本地的 No...
    linshuai阅读 487评论 0 4
  • 利用IndexedDB API,可以在客户端构建一个事务性数据库,可以在客户端存储大量的结构化数据。 创建并连接到...
    幸福镰刀阅读 2,170评论 1 1
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,633评论 18 139
  • 非关系型数据库 storage最多只能存放2.5M-5M数据,所以浏览器本地储存需要使用indexedDB ind...
    _Enco_阅读 261评论 0 0
  • 和N的第一次跑步,因为他的大长腿,我只好紧紧跟着,于是跑速也相应提高,跑了四十多分钟,但一路上也还能跟上。对我来说...
    好菇凉啊光芒万丈阅读 256评论 0 0