indexedDB 前端存儲方案

一、前言

廣東靚仔在2018年了解過indexedDB,不過沒有在日常的開發(fā)使用。最近又看到一些關(guān)于indexedDB的文章,這里跟小伙伴們一起分享下。

二、特性對比


我們一起來看看cookie、localStorage、sessionStorage、indexDB的特性對比。



231220bk-1.png


caniuse上查看 indexedDB 支持情況,目前瀏覽器支持情況良好。

231220bk-2.png


三、IndexedDB簡介

是什么

IndexedDB 是一種使用瀏覽器存儲大量數(shù)據(jù)的方法.它創(chuàng)造的數(shù)據(jù)可以被查詢,并且可以離線使用. IndexedDB對于那些需要存儲大量數(shù)據(jù),或者是需要離線使用的程序是非常有效的解決方法. --- MDN

簡單而言,indexedDB就是一個基于事務(wù)操作的key-value型數(shù)前端數(shù)據(jù)庫.其API大多是異步的

特點

鍵值對儲存 IndexedDB 內(nèi)部采用對象倉庫(object store)存放數(shù)據(jù)。所有類型的數(shù)據(jù)都可以直接存入,包括 JavaScript 對象。對象倉庫中,數(shù)據(jù)以"鍵值對"的形式保存,每一個數(shù)據(jù)記錄都有對應(yīng)的主鍵,主鍵是獨一無二的,不能有重復(fù),否則會拋出一個錯誤。

異步 IndexedDB 操作時不會鎖死瀏覽器,用戶依然可以進行其他操作,這與 LocalStorage 形成對比,后者的操作是同步的。異步設(shè)計是為了防止大量數(shù)據(jù)的讀寫,拖慢網(wǎng)頁的表現(xiàn)。

支持事務(wù) IndexedDB 支持事務(wù)(transaction),這意味著一系列操作步驟之中,只要有一步失敗,整個事務(wù)就都取消,數(shù)據(jù)庫回滾到事務(wù)發(fā)生之前的狀態(tài),不存在只改寫一部分數(shù)據(jù)的情況。

同源限制 IndexedDB 受到同源限制,每一個數(shù)據(jù)庫對應(yīng)創(chuàng)建它的域名。網(wǎng)頁只能訪問自身域名下的數(shù)據(jù)庫,而不能訪問跨域的數(shù)據(jù)庫。

支持二進制儲存 IndexedDB 不僅可以儲存字符串,還可以儲存二進制數(shù)據(jù)(ArrayBuffer 對象和 Blob 對象。

儲存空間大 IndexedDB 的儲存空間比 LocalStorage 大得多,一般來說不少于 250MB,甚至沒有上限。儲 存 在 電 腦 上 中 的 位 置 為 C:\Users\當 前 的 登 錄 用 戶\AppData\Local\Google\Chrome\User Data\Default\IndexedDB

核心概念

數(shù)據(jù)庫:IDBDatabase 對象,數(shù)據(jù)庫有版本概念,同一時刻只能有一個版本,每個域名可以建多個數(shù)據(jù)庫

對象倉庫:IDBObjectStore 對象,類似于關(guān)系型數(shù)據(jù)庫的表格

索引:IDBIndex 對象,可以在對象倉庫中,為不同的屬性建立索引,主鍵建立默認索引

事務(wù):IDBTransaction 對象,增刪改查都需要通過事務(wù)來完成,事務(wù)對象提供了error,abord,complete三個回調(diào)方法,監(jiān)聽操作結(jié)果

操作請求:IDBRequest 對象

指針:IDBCursor 對象

主鍵集合:IDBKeyRange 對象,主鍵是默認建立索引的屬性,可以取當前層級的某個屬性,也可以指定下一層對象的屬性,還可以是一個遞增的整數(shù)

四、indexedDB的使用

創(chuàng)建數(shù)據(jù)庫 & 新建表和索引

var request = window.indexedDB.open('myDatabase', 1);


request.onerror = function(error) {

  console.log('IndexedDB 打開失敗', error);

};


request.onsuccess = function(res) {

  console.log('IndexedDB 打開成功', res);

  db = res.target.result;

};


request.onupgradeneeded = function(res) {

  console.log('IndexedDB 升級成功', res);

  db = res.target.result;

  db_table = db.createObjectStore('myDatabase', { keyPath: 'id' });

  db_table.createIndex('indexName', 'name', { unique: false });

};

操作數(shù)據(jù)api

add() : 增加數(shù)據(jù)。接收一個參數(shù),為需要保存到對象倉庫中的對象。

put() : 增加或修改數(shù)據(jù)。接收一個參數(shù),為需要保存到對象倉庫中的對象。

get() : 獲取數(shù)據(jù)。接收一個參數(shù),為需要獲取數(shù)據(jù)的主鍵值。

delete() : 刪除數(shù)據(jù)。接收一個參數(shù),為需要獲取數(shù)據(jù)的主鍵值。

Tips:  add 和 put 的作用類似,區(qū)別在于 put 保存數(shù)據(jù)時,如果該數(shù)據(jù)的主鍵在數(shù)據(jù)庫中已經(jīng)有相同主鍵的時候,則會修改數(shù)據(jù)庫中對應(yīng)主鍵的對象,而使用 add 保存數(shù)據(jù),如果該主鍵已經(jīng)存在,則保存失敗。


添加數(shù)據(jù)


var store = db.transaction(['myDatabase'], 'readwrite').objectStore('myDatabase');


//方法添加數(shù)據(jù)

var request = store.add({

  id: 1,

  name: '廣東靚仔',

  tip: 'qianduanzaocha',

});


//添加成功

request.onsuccess = function(event) {

  console.log('數(shù)據(jù)添加成功', event);

};


//添加失敗

request.onerror = function(event) {

  console.log('數(shù)據(jù)添加失敗', event);

};

獲取數(shù)據(jù)


var store = db.transaction(['myDatabase']).objectStore('myDatabase');


// get方法獲取數(shù)據(jù),params 數(shù)據(jù)的主鍵

 

var request = store.get(1);


// 獲取成功

request.onsuccess = function(event) {

  if (event.target.result) {

    console.log('數(shù)據(jù)獲取成功', event.target.result);

  } else {

    console.log('未獲取到數(shù)據(jù)');

  }

};


// 獲取失敗

request.onerror = function(event) {

  console.log('數(shù)據(jù)獲取失敗', event);

};


刪除數(shù)據(jù)


var store = db.transaction(['myDatabase'], 'readwrite').objectStore('myDatabase');


// delete方法刪除數(shù)據(jù),params 數(shù)據(jù)的主鍵

var request = store.delete(1); 


// 刪除成功

request.onsuccess = function (event) {

    console.log('數(shù)據(jù)刪除成功',event);

};


// 刪除失敗

request.onerror = function (event) {

    console.log('數(shù)據(jù)刪除失敗',event);

};

更新數(shù)據(jù)


var store = db.transaction(['myDatabase'], 'readwrite').objectStore('myDatabase');


// put方法根據(jù)主鍵更新數(shù)據(jù),params 數(shù)據(jù)的主鍵

var request = store.put({

  id: 2,

  name: '靚仔',

  tip: 'guangdong',

});


// 更新成功

request.onsuccess = function(event) {

  console.log('數(shù)據(jù)更新成功', event);

};


// 更新失敗

request.onerror = function(event) {

  console.log('數(shù)據(jù)更新失敗', event);

};

使用游標


var store = db.transaction(['myDatabase']).objectStore('myDatabase');


// index方法獲取索引對象,get方法獲取數(shù)據(jù),params 數(shù)據(jù)的索引

var request = store.index('indexName').get('靚仔'); 


// 獲取成功

request.onsuccess = function (event) {

     console.log('通過索引獲取數(shù)據(jù)成功',event.target.result);

};


// 獲取失敗

request.onerror = function (event) {

    console.log('通過索引獲取數(shù)據(jù)失敗',event);

};

getAll()是用來獲取整張表的數(shù)據(jù)


IDBKeyRange對象


索引的有用之處,還在于可以指定讀取數(shù)據(jù)的范圍。這需要用到瀏覽器原生的IDBKeyRange對象。(指定條件獲取數(shù)據(jù))


IDBKeyRange對象的作用是生成一個表示范圍的Range對象。生成方法有四種:


lowerBound方法:指定范圍的下限。


upperBound方法:指定范圍的上限。


bound方法:指定范圍的上下限。


only方法:指定范圍中只有一個值。


// All keys ≤ x 

var r1 = IDBKeyRange.upperBound(x);


// All keys < x 

var r2 = IDBKeyRange.upperBound(x, true);


// All keys ≥ y 

var r3 = IDBKeyRange.lowerBound(y);


// All keys > y 

var r4 = IDBKeyRange.lowerBound(y, true);


// All keys ≥ x && ≤ y 

var r5 = IDBKeyRange.bound(x, y);


// All keys > x &&< y 

var r6 = IDBKeyRange.bound(x, y, true, true);


// All keys > x && ≤ y 

var r7 = IDBKeyRange.bound(x, y, true, false);


// All keys ≥ x &&< y 

var r8 = IDBKeyRange.bound(x, y, false, true);


// The key = z 

var r9 = IDBKeyRange.only(z);

參考鏈接:


https://www.w3cschool.cn/javascript_guide/javascript_guide-rcfy26a4.html#toc9


https://developer.mozilla.org/en-US/docs/Web/API/IDBObjectStore/transaction


https://caniuse.com/?search=indexedDB




作者:廣東靚仔


歡迎關(guān)注:前端早茶