IndexedDB

indexedDB 的整体架构,是由一系列的单独概念串联而成,去不概念如下列表。

  • IndexedDBRequest
  • IndexedDBFactory
  • IndexedDBDatabase
  • IndexedDBObjectStore
  • IndexedDBIndex
  • IndexedDBKeyRange
  • IndexedDBCursor
  • IndexedDBTransaction

整体逻辑图如下:

IndexedDB逻辑图

下文主要介绍了 indexedDB 的基本概念,以及在实际应用中的实操代码。

  • indexedDB 基础概念。在 indexedDB 里面会根据索引 index 来进行整体数据结构的划分。
  • indexedDB 数据库的更新是一个非常蛋疼的事情,因为,Web 的灵活性,你既需要做好向上版本的更新,也需要完善向下版本的容错性。
  • indexedDB 高效索引机制,在内部,indexedDB 已经提供了 index、cursor 等高效的索引机制,推荐不要直接将所有数据都取回来,再进行筛选,而是直接利用 cursor 进行。
  • 最后推荐几个常用库

离线存储

IndexedDB 可以存储非常多类型的数据,比如 ObjectFileBlob 等,里面的存储结构是根据 Database 来进行存储的。每个 DB 里面可以有不同的 Object Stores。具体结构如下图:

IndexedDB结构图

并且,我们可以给 key 设定相关特定的值,然后在索引的时候,可以直接通过 key 得到具体的内容。使用 IndexDB 需要注意,其遵循的是 同域原则

基本概念

在 indexDB 中,有几个基本的操作对象:

Database

Database 通过 open 方法直接打开,可以得到一个实例的 DB。每个页面可以创建多个 DB,不过一般都是一个。

idb.open(name, version, upgradeCallback);

Object Store

Object Store 这个就是 DB 里面具体存储的对象。这个可以对应于 SQL 里面的 Table(表)内容。其存储的结构为:

Index

index 有点类似于外链,它本身是一种 Object Store,主要是用来在本体的 Store 中,索引另外 Object Store 里面的数据。需要区别的是,keyindex 是不一样的。

可以参考:DEMO1DEMO2

如下图表示:

// 创建 index
const myIndex = objectStore.index('lName');

Transaction

transaction 事务其实就是一系列 CRUD 的集合内容。如果其中一个环节失败了,那么整个事务的处理都会被取消。例如:

var trans1 = db.transaction('foo', 'readwrite');
var trans2 = db.transaction('foo', 'readwrite');
var objectStore2 = trans2.objectStore('foo');
var objectStore1 = trans1.objectStore('foo');
objectStore2.put('2', 'key');
objectStore1.put('1', 'key');

Cursor

cursor 主要是用来遍历 DB 里面的数据内容。主要是通过 openCursor 来进行控制。

function displayData() {
var transaction = db.transaction(['rushAlbumList'], 'readonly');
var objectStore = transaction.objectStore('rushAlbumList');
objectStore.openCursor().onsuccess = function (event) {
var cursor = event.target.result;
if (cursor) {
var listItem = document.createElement('li');
listItem.innerHTML = cursor.value.albumTitle + ', ' + cursor.value.year;
list.appendChild(listItem);
cursor.continue();
} else {
console.log('Entries all displayed.');
}
};
}

基本用法

创建数据库

const createUpdateStore = function (name, version = 1) {
const request = window.indexdDB.open(name, version);
request.onsuccess = function (event) {
console.log('open success');
};
request.onerror = function (event) {
console.log('open fail');
};
request.onupgradeneeded = function (event) {
const db = event.target.result;
if (!db.objectStoreNames.contains(name)) {
// 创建仓库对象(创建表格)
// 这里我将主键设为 ID
const objectStore = db.createObjectStore(name, {
keyPath: 'id',
autoIncrement: true,
});
}
};
};

添加数据

const addDataStore = function (storeName, data, verson) {
return new Promise((resolve, reject) => {
let databaseName = storeName;
let databaseVersion = verson || 1;
let db;
let request = indexedDB.open(databaseName, databaseVersion);
request.onsuccess = function (event) {
db = event.target.result;
db = event.target.result;
// 将数据保存到新建的对象仓库
let objectStore = db.transaction(databaseName, 'readwrite').objectStore(databaseName);
if (uf.utils.typeof(data, 'array')) {
data.forEach(function (dataItem) {
// 添加一条数据
objectStore.add(dataItem);
});
resolve();
} else {
// 添加一条数据
objectStore.add(data);
resolve();
}
};
request.error = function () {
reject();
};
request.onupgradeneeded = function (event) {
let db = event.target.result;
if (!db.objectStoreNames.contains(storeName)) {
// 创建仓库对象(创建表格)
// 这里我将主键设置为id
let objectStore = db.createObjectStore(storeName, {
keyPath: 'id',
autoIncrement: true,
});
}
};
});
};

获取数据

let getStoreData = function (name, key = 1) {
console.log('getStoreData');
return new Promise((resolve, reject) => {
let request = indexedDB.open(name);
request.onsuccess = function (event) {
let db = event.target.result;
let req;
try {
req = db.transaction(name, 'readwrite').objectStore(name).get(key); // 这里的“1”也是主键的键值
} catch (e) {
reject('用户失败');
}
if (!req) {
return;
}
req.onsuccess = function () {
resolve(req.result);
};
req.onerror = function () {
reject('获取失败');
};
};
request.onupgradeneeded = function (event) {
let db = event.target.result;
if (!db.objectStoreNames.contains(name)) {
// 创建仓库对象(创建表格)
// 这里我将主键设置为id
let objectStore = db.createObjectStore(name, {
keyPath: 'id',
autoIncrement: true,
});
}
};
});
};

删除数据

const delectStoreData = function (name, key) {
console.log('delectStoreData');
return new Promise((resolve, reject) => {
let databaseName = name;
let db;
let request = window.indexedDB.open(databaseName);
request.onsuccess = function (event) {
db = event.target.result;
// 这里指定的是主键的键值
let req = db.transaction(databaseName, 'readwrite').objectStore(databaseName).delete(key);
req.onsuccess = function () {
resolve('删除成功');
};
req.onerror = function () {
reject('删除失败');
};
};
});
};

更新数据

const updateStoreData = function (storeName, newData, key) {
console.log('updateStoreData');
return new Promise((resolve, reject) => {
let request = window.indexedDB.open(storeName);
let db;
request.onsuccess = function (event) {
db = event.target.result;
let transaction = db.transaction(storeName, 'readwrite');
let store = transaction.objectStore(storeName);
let storeData = store.get(key);
storeData.onsuccess = function (e) {
let data = e.target.result || {};
for (a in newData) {
data[a] = newData[a];
}
store.put(data);
resolve();
};
};
request.onupgradeneeded = function (event) {
let db = event.target.result;
if (!db.objectStoreNames.contains(storeName)) {
// 创建仓库对象(创建表格)
// 这里我将主键设置为id
let objectStore = db.createObjectStore(storeName, {
keyPath: 'id',
autoIncrement: true,
});
}
};
});
};

遍历获取

const storeDataList = function (storeName) {
console.log('storeDataList');
return new Promise((resolve, reject) => {
let request = window.indexedDB.open(storeName);
let db;
request.onsuccess = function (event) {
db = event.target.result;
let transaction = db.transaction(storeName);
let store = transaction.objectStore(storeName);
// 打开游标
let cursor = store.openCursor();
let dataList = new Array();
cursor.onsuccess = function (e) {
var cursorVal = e.target.result;
if (cursorVal) {
dataList.push(cursorVal.value);
cursorVal.continue();
} else {
// 遍历结束
resolve(dataList);
}
};
};
request.onupgradeneeded = function (event) {
let db = event.target.result;
if (!db.objectStoreNames.contains(storeName)) {
// 创建仓库对象(创建表格)
// 这里我将主键设置为id
let objectStore = db.createObjectStore(storeName, {
keyPath: 'id',
autoIncrement: true,
});
}
};
});
};

批量删除

const batchDelete = function (storeName, keys) {
console.log('batchDelete');
let allKeys = keys.map((item) => {
item = +item;
return delectStoreData(storeName, item);
});
return allKeys;
/* Promise.all(allKeys).then(data => {
console.log(data);
resolve(data);
});*/
};

第三方依赖库

如果碰到前端频繁存储操作或者大文件缓存的需求,可以考虑使用 IndexedDB,当然项目中推荐直接使用第三方库:

名称说明
zangodbZangoDB 是一个类似 MongoDB 的 HTML5 IndexedDB 接口库,支持 MongoDB 的大多数常见功能包括 filer、sorting、updating 和 aggregation,可在 Web 浏览器中使用
dexie.jsIndexedDB 的简约包装器

参考资料