最近业务需求中需要使用到数据库,在翻看
vscode
源码过程中,发现vscode一共用了两种数据库存储数据,SQLite
和IndexedDB
,本文主要对IndexedDB做下讲解
2008 年左右,网站 、 论坛、社交网络开始高速发展,传统的关系型数据库
在存储及处理数据的时候受到了很大的挑战 ,其中主要体现在以下几点:
在很多 互联网应用场景下 , 对数据联表的查询需求不是那么强烈 ,也并不需要在数据写入后立刻读取,但对数据的读取和并发写入速度有非常高的要求 。 在这样的情况下 ,非关系型数据库
得到高速的发展 。
关系型数据库:
非关系型数据库:
随着浏览器的功能不断增强,越来越多的网站开始考虑,将大量数据储存在客户端,这样可以减少从服务器获取数据,直接从本地获取数据。
所以,需要一种新的解决方案,这就是 IndexedDB 诞生的背景。
通俗地说,IndexedDB 就是浏览器提供的本地数据库,一个基于 JavaScript 的面向对象数据库,它可以被网页脚本创建和操作。IndexedDB 允许储存大量数据,提供查找接口,还能建立索引。这些都是 LocalStorage 所不具备的。就数据库类型而言,IndexedDB 不属于关系型数据库(不支持 SQL 查询语句),更接近 NoSQL 数据库。
IndexedDB 具有以下特点。
(1)键值对储存。 IndexedDB 内部采用对象仓库(object store)存放数据。所有类型的数据都可以直接存入,包括 JavaScript 对象。对象仓库中,数据以"键值对"的形式保存,每一个数据记录都有对应的主键,主键是独一无二的,不能有重复,否则会抛出一个错误。
(2)异步。 IndexedDB 操作时不会锁死浏览器,用户依然可以进行其他操作,这与 LocalStorage 形成对比,后者的操作是同步的。异步设计是为了防止大量数据的读写,拖慢网页的表现。
(3)支持事务。 IndexedDB 支持事务(transaction),这意味着一系列操作步骤之中,只要有一步失败,整个事务就都取消,数据库回滚到事务发生之前的状态,不存在只改写一部分数据的情况。
(4)同源限制 IndexedDB 受到同源限制,每一个数据库对应创建它的域名。网页只能访问自身域名下的数据库,而不能访问跨域的数据库。
(5)储存空间大 IndexedDB 的储存空间比 LocalStorage 大得多,一般来说不少于 250MB,甚至没有上限。
(6)支持二进制储存。 IndexedDB 不仅可以储存字符串,还可以储存二进制数据(ArrayBuffer 对象和 Blob 对象)。
我们以类的形式对它封装:
export class IndexedDB {static async create(name: string, version: number | undefined, stores: string[]): Promise {const database = await IndexedDB.openDatabase(name, version, stores);return new IndexedDB(database, name);}static async openDatabase(name: string, version: number | undefined, stores: string[]): Promise {mark(`code/willOpenDatabase/${name}`);try {return await IndexedDB.doOpenDatabase(name, version, stores);} catch (err) {if (err instanceof MissingStoresError) {console.info(`Attempting to recreate the IndexedDB once.`, name);try {// Try to delete the dbawait IndexedDB.deleteDatabase(err.db);} catch (error) {console.error(`Error while deleting the IndexedDB`, getErrorMessage(error));throw error;}return await IndexedDB.doOpenDatabase(name, version, stores);}throw err;} finally {mark(`code/didOpenDatabase/${name}`);}}private static doOpenDatabase(name: string, version: number | undefined, stores: string[]): Promise {return new Promise((c, e) => {const request = window.indexedDB.open(name, version);request.onerror = () => e(request.error);request.onsuccess = () => {const db = request.result;for (const store of stores) {if (!db.objectStoreNames.contains(store)) {console.error(`Error while opening IndexedDB. Could not find '${store}'' object store`);e(new MissingStoresError(db));return;}}c(db);};request.onupgradeneeded = () => {const db = request.result;for (const store of stores) {if (!db.objectStoreNames.contains(store)) {db.createObjectStore(store);}}};});}private static deleteDatabase(indexedDB: IDBDatabase): Promise {return new Promise((c, e) => {// Close any opened connectionsindexedDB.close();// Delete the dbconst deleteRequest = window.indexedDB.deleteDatabase(indexedDB.name);deleteRequest.onerror = (err) => e(deleteRequest.error);deleteRequest.onsuccess = () => c();});}private database: IDBDatabase | null = null;private readonly pendingTransactions: IDBTransaction[] = [];constructor(database: IDBDatabase, private readonly name: string) {this.database = database;}hasPendingTransactions(): boolean {return this.pendingTransactions.length > 0;}close(): void {if (this.pendingTransactions.length) {this.pendingTransactions.splice(0, this.pendingTransactions.length).forEach(transaction => transaction.abort());}if (this.database) {this.database.close();}this.database = null;}runInTransaction(store: string, transactionMode: IDBTransactionMode, dbRequestFn: (store: IDBObjectStore) => IDBRequest[]): Promise;runInTransaction(store: string, transactionMode: IDBTransactionMode, dbRequestFn: (store: IDBObjectStore) => IDBRequest): Promise;async runInTransaction(store: string, transactionMode: IDBTransactionMode, dbRequestFn: (store: IDBObjectStore) => IDBRequest | IDBRequest[]): Promise {if (!this.database) {throw new Error(`IndexedDB database '${this.name}' is not opened.`);}const transaction = this.database.transaction(store, transactionMode);this.pendingTransactions.push(transaction);return new Promise((c, e) => {transaction.oncomplete = () => {if (isArray(request)) {c(request.map(r => r.result));} else {c(request.result);}};transaction.onerror = () => e(transaction.error);const request = dbRequestFn(transaction.objectStore(store));}).finally(() => this.pendingTransactions.splice(this.pendingTransactions.indexOf(transaction), 1));}async getKeyValues(store: string, isValid: (value: unknown) => value is V): Promise
在服务里面使用
import { IndexedDB } from 'vs/base/browser/indexedDB'; //引入
private readonly whenConnected!: Promise; //定义私有变量存储数据库
初始化 constructor
this.whenConnected = this.connect(); //链接数据库
private async connect(): Promise {try {return await IndexedDB.create('indexedDB-test', undefined, ['test-store1']);} catch (error) {throw error;}
}
在方法里面使用
let indexedDB = await this.whenConnected;// 增
try {await indexedDB.runInTransaction('test-store1', 'readwrite', store => store.add({ 'test': '222' }, 'key3'));
} catch (e) {console.log('存储数据出错')
}// 删
try {await indexedDB.runInTransaction('test-store1', 'readwrite', store => store.delete('key3'));
} catch (e) {console.log('删除数据出错');
}// 改
try {await indexedDB.runInTransaction('test-store1', 'readwrite', store => store.put({ 'lichangwei': '123' }, 'key3'));
} catch (e) {console.log('删除数据出错');
}// 查
const value = await indexedDB.runInTransaction('test-store1', 'readwrite', store => store.getAll());
有问题欢迎留言
参考阮一峰日志
下一篇:Kotlin语法-Day2