简介

IndexedDB是一种在浏览器端存储数据的形式。既然称之为DB,是因为它丰盛了客户端的查问形式,并且因为是本地存储,能够无效的缩小网络对页面数据的影响。

有了IndexedDB,浏览器能够存储更多的数据,从而丰盛了浏览器端的利用类型。

IndexedDB简介

IndexedDB和传统的关系型数据不同的是,它是一个key-value型的数据库。

value能够是简单的构造体对象,key能够是对象的某些属性值也能够是其余的对象(包含二进制对象)。你能够应用对象中的任何属性做为index,以放慢查找。

IndexedDB是自带transaction的,所有的数据库操作都会绑定到特定的事务上,并且这些事务是主动提交了,IndexedDB并不反对手动提交事务。

IndexedDB API大部分都是异步的,在应用异步办法的时候,API不会立马返回要查问的数据,而是返回一个callback。

异步API的实质是向数据库发送一个操作申请,当操作实现的时候,会收到一个DOM event,通过该event,咱们会晓得操作是否胜利,并且取得操作的后果。

IndexedDB是一种 NoSQL 数据库,和关系型数据库不同的是,IndexedDB是面向对象的,它存储的是Javascript对象。

IndexedDB还有一个很重要的特点是其同源策略,每个源都会关联到不同的数据库汇合,不同源是不容许拜访其余源的数据库,从而保障了IndexedDB的安全性。

IndexedDB的应用

这一节,咱们将会以具体的例子来解说如何应用IndexedDB。

IndexedDB的浏览器反对

不同的浏览器对于IndexedDB有不同的实现,失常来说,咱们能够应用window.indexedDB来获取到浏览器的indexedDB对象。然而对于某些浏览器来说,还没有应用规范的window.indexedDB,而是用带前缀的实现。

所以咱们在应用过程中通常须要进行判断和转换:

// In the following line, you should include the prefixes of implementations you want to test.window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;// DON'T use "var indexedDB = ..." if you're not in a function.// Moreover, you may need references to some window.IDB* objects:window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction || {READ_WRITE: "readwrite"}; // This line should only be needed if it is needed to support the object's constants for older browserswindow.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;// (Mozilla has never prefixed these objects, so we don't need window.mozIDB*)

下面咱们从window获取了indexedDB,IDBTransaction和IDBKeyRange三个对象。

其中indexedDB示意的是数据库的连贯。IDBTransaction示意的是transaction,而IDBKeyRange则是用从数据库的某个特定key range中取出数据。

然而,通常来说带前缀的实现个别都是不稳固的,所以咱们通常不倡议在正式环境中应用,所以如果不反对规范表达式的话,须要间接报错:

if (!window.indexedDB) {    console.log("Your browser doesn't support a stable version of IndexedDB. Such and such feature will not be available.");}

创立IndexedDB

要应用IndexedDB,咱们首先须要open it:

// Let us open our databasevar request = window.indexedDB.open("MyTestDatabase", 3);

open办法返回一个IDBOpenDBRequest对象,同时这是一个异步操作,open操作并不会立马关上数据库或者开启事务,咱们能够通过监听request的事件来进行相应的解决。

open办法传入两个参数,第一个参数是数据库的名字,第二个参数是数据库的版本号。

当你创立一个新的数据库或者降级一个现有的数据库版本的时候,将会触发一个onupgradeneeded事件,并在事件中传入IDBVersionChangeEvent,咱们能够通过event.target.result来获取到IDBDatabase对象,而后通过这个对象来进行数据库的版本升级操作。如下所示:

// This event is only implemented in recent browsers   request.onupgradeneeded = function(event) {   // Save the IDBDatabase interface   var db = event.target.result;  // Create an objectStore for this database  var objectStore = db.createObjectStore("name", { keyPath: "myKey" });};

留神,这里的版本号是一个整数。如果你传入一个float,那么将会对该float进行取整操作。

有了request,咱们能够通过监听onerror或者onsuccess事件来进行相应的解决。

var db;var request = indexedDB.open("MyTestDatabase");request.onerror = function(event) {  console.log("Why didn't you allow my web app to use IndexedDB?!");};request.onsuccess = function(event) {  db = event.target.result;};

拿到db对象之后,咱们能够设置全局的异样解决:

db.onerror = function(event) {  // Generic error handler for all errors targeted at this database's  // requests!  console.error("Database error: " + event.target.errorCode);};

IndexedDB中的table叫做object stores,和关系型数据库中的table一样,object stores中的每一个对象都和一个key相关联,和key相干的有两个概念 key path 和 key generator.

如果存储的是javascript Object对象,那么能够指定该对象中的某一个属性作为key path,那么这个属性将会被作为key。

如果没有指定key path,那么存储的Object能够是任何对象,甚至是根底类型比方数字和String。

而key generator就是key的生成器。

如果咱们想要存储这样的数据:

// This is what our customer data looks like.const customerData = [  { ssn: "444-44-4444", name: "Bill", age: 35, email: "bill@company.com" },  { ssn: "555-55-5555", name: "Donna", age: 32, email: "donna@home.org" }];

看一下对应的数据库操作是怎么样的:

const dbName = "the_name";var request = indexedDB.open(dbName, 2);request.onerror = function(event) {  // Handle errors.};request.onupgradeneeded = function(event) {  var db = event.target.result;  // Create an objectStore to hold information about our customers. We're  // going to use "ssn" as our key path because it's guaranteed to be  // unique - or at least that's what I was told during the kickoff meeting.  var objectStore = db.createObjectStore("customers", { keyPath: "ssn" });  // Create an index to search customers by name. We may have duplicates  // so we can't use a unique index.  objectStore.createIndex("name", "name", { unique: false });  // Create an index to search customers by email. We want to ensure that  // no two customers have the same email, so use a unique index.  objectStore.createIndex("email", "email", { unique: true });  // Use transaction oncomplete to make sure the objectStore creation is   // finished before adding data into it.  objectStore.transaction.oncomplete = function(event) {    // Store values in the newly created objectStore.    var customerObjectStore = db.transaction("customers", "readwrite").objectStore("customers");    customerData.forEach(function(customer) {      customerObjectStore.add(customer);    });  };};

咱们须要在onupgradeneeded事件中解决所有的schema相干的操作。

首先应用db.createObjectStore创立了一个customers的ObjectStore,并且应用了对象的keypath作为key。

除了key之外,咱们创立了两个index,以进步查问速度。

最初咱们监听transaction.oncomplete事件,并在外面退出存储object的操作。

下面的代码中,咱们应用了keyPath作为key。

上面是一个应用key Generator的例子:

 var objStore = db.createObjectStore("names", { autoIncrement : true });

indexdb中的CURD

indexedDB的所有操作都须要在事务中,咱们看一个开启事务的操作:

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

下面的例子中应用readwrite来操作customers ObjectStore。

transaction接管两个参数,第一个参数是一个数组,数组中是这个trans中将会解决的ObjectStores,第二个参数是解决的模式。

有了transaction之后,咱们能够监听事务的complete和error操作,而后就能够进行add操作了:

// Do something when all the data is added to the database.transaction.oncomplete = function(event) {  console.log("All done!");};transaction.onerror = function(event) {  // Don't forget to handle errors!};var objectStore = transaction.objectStore("customers");customerData.forEach(function(customer) {  var request = objectStore.add(customer);  request.onsuccess = function(event) {    // event.target.result === customer.ssn;  };});

下面的例子中,咱们应用了add办法,add的前提是数据库中并不存在雷同key的对象。除了add办法之外,咱们还能够应用put办法,put办法次要用来进行更新操作。

再看一个删除的操作:

var request = db.transaction(["customers"], "readwrite")                .objectStore("customers")                .delete("444-44-4444");request.onsuccess = function(event) {  // It's gone!};

当初咱们的数据库曾经有了数据,咱们看下怎么进行查问:

var transaction = db.transaction(["customers"]);var objectStore = transaction.objectStore("customers");var request = objectStore.get("444-44-4444");request.onerror = function(event) {  // Handle errors!};request.onsuccess = function(event) {  // Do something with the request.result!  console.log("Name for SSN 444-44-4444 is " + request.result.name);

这里,咱们间接应用了db.transaction,默认状况下是readonly模式的。

上面是一个更新的例子:

var objectStore = db.transaction(["customers"], "readwrite").objectStore("customers");var request = objectStore.get("444-44-4444");request.onerror = function(event) {  // Handle errors!};request.onsuccess = function(event) {  // Get the old value that we want to update  var data = event.target.result;    // update the value(s) in the object that you want to change  data.age = 42;  // Put this updated object back into the database.  var requestUpdate = objectStore.put(data);   requestUpdate.onerror = function(event) {     // Do something with the error   };   requestUpdate.onsuccess = function(event) {     // Success - the data is updated!   };};

更新咱们应用的是put办法。

应用游标cursor

indexedDB反对游标操作,咱们能够应用cursor来遍历objectStore的数据:

var objectStore = db.transaction("customers").objectStore("customers");objectStore.openCursor().onsuccess = function(event) {  var cursor = event.target.result;  if (cursor) {    console.log("Name for SSN " + cursor.key + " is " + cursor.value.name);    cursor.continue();  }  else {    console.log("No more entries!");  }};

openCursor能够承受多个参数,第一个参数能够承受key的查问范畴,第二个参数用来指定遍历的方向。如果两个参数都为空的话,默认是所有的数据的以升序的程序遍历。

如果想遍历下一个游标,则能够调用cursor.continue。

咱们看一下两个参数的游标应用:

// Only match "Donna"var singleKeyRange = IDBKeyRange.only("Donna");// Match anything past "Bill", including "Bill"var lowerBoundKeyRange = IDBKeyRange.lowerBound("Bill");// Match anything past "Bill", but don't include "Bill"var lowerBoundOpenKeyRange = IDBKeyRange.lowerBound("Bill", true);// Match anything up to, but not including, "Donna"var upperBoundOpenKeyRange = IDBKeyRange.upperBound("Donna", true);// Match anything between "Bill" and "Donna", but not including "Donna"var boundKeyRange = IDBKeyRange.bound("Bill", "Donna", false, true);// To use one of the key ranges, pass it in as the first argument of openCursor()/openKeyCursor()index.openCursor(boundKeyRange, "prev").onsuccess = function(event) {  var cursor = event.target.result;  if (cursor) {    // Do something with the matches.    cursor.continue();  }};

除了openCursor,咱们还能够通过应用openKeyCursor来遍历KeyCursor:

// Using a normal cursor to grab whole customer record objectsindex.openCursor().onsuccess = function(event) {  var cursor = event.target.result;  if (cursor) {    // cursor.key is a name, like "Bill", and cursor.value is the whole object.    console.log("Name: " + cursor.key + ", SSN: " + cursor.value.ssn + ", email: " + cursor.value.email);    cursor.continue();  }};// Using a key cursor to grab customer record object keysindex.openKeyCursor().onsuccess = function(event) {  var cursor = event.target.result;  if (cursor) {    // cursor.key is a name, like "Bill", and cursor.value is the SSN.    // No way to directly get the rest of the stored object.    console.log("Name: " + cursor.key + ", SSN: " + cursor.primaryKey);    cursor.continue();  }};

除此之外,咱们还能够间接通过index来进行查问:

var index = objectStore.index("name");index.get("Donna").onsuccess = function(event) {  console.log("Donna's SSN is " + event.target.result.ssn);};

要应用index的前提就是须要在request.onupgradeneeded中创立index。

本文作者:flydean程序那些事

本文链接:http://www.flydean.com/indexeddb-kickoff/

本文起源:flydean的博客

欢送关注我的公众号:「程序那些事」最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!