1. 程式人生 > >客戶端持久化資料庫---indexedDB使用

客戶端持久化資料庫---indexedDB使用

閱讀目錄

  • 一:什麼是indexedDB資料庫?
  • 二:IndexedDB資料庫操作 2.1 開啟或建立資料庫 2.2 建立物件倉庫(或叫建立表) 2.3 建立索引 2.4 新增資料 2.5 讀取資料 2.6 遍歷資料 2.7 更新資料 2.8 刪除資料 2.9 使用索引 2.10 清空所有的資料 clear() 方法
回到頂部

一:什麼是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 下的資料如下所示:

可以看到我們所有的資料都被清空了。