indexedDB 前端存儲方案
一、前言
廣東靚仔在2018年了解過indexedDB,不過沒有在日常的開發(fā)使用。最近又看到一些關(guān)于indexedDB的文章,這里跟小伙伴們一起分享下。
二、特性對比
我們一起來看看cookie、localStorage、sessionStorage、indexDB的特性對比。
caniuse上查看 indexedDB 支持情況,目前瀏覽器支持情況良好。
三、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)注:前端早茶