前言
IndexDB是一个相对底层一点API。通常用于存储大量结构化数据,包括文件、Blob等。IndexDB的API使用索引来实现对数据的高性能读取。
localStorage、sessionStorage是浏览器最常用的数据存储方案。两个各有优缺点。
- localStorage支持持久性存储,浏览器会话结束依旧可以存储数据在浏览器内存中,方便下次数据读取与使用。但缺点是存储的大小有限制,不同的浏览器基于的存储空间有所区别,但是都在5M左右。对于普通的业务场景足够胜任。但是对于通讯应用,存储对话记录这种高密度数据存储的场景来说,内存空间完全是不够用的。
- sessionStorage支持会话存储,浏览器相同域名会话窗口之间支持内存数据共享。当会话结束之后,关闭会话窗口,内存数据会被清除。缺点很明显,会话不支持永久存储,同时内存空间也很小。
三者都是基于源(Origin)的对象存储。源(Origin)指的是协议、域名、端口。不同的源数据是不支持共享的,相互之间保持独立,能够完美的避免不同源的数据污染。
www.baidu.com
和www.google.com
两个是不同的源,数据均不支持共享。
如果不同源数据支持共享会造成严重的数据污染,如下面的场景:
www.baidu.com
源存储了一个数据 键为a,值为b
www.google.com
源存储了一个数据 键为a,值为b
两键值对修改会造成相互相应,从而引发数据污染。
IndexDB优势
两者最显著的缺点是
- 存储空间太小
- 键值对总是以字符串形式存储,不支持其他数据格式,如Blob、ArrayBuffer
- 不支持异步操作,数据量大时进行I/O操作性能体验差
IndexDb成为了如上问题的解决方案。
空间大小
IndexDB存储空间根据不同的设备而定,因为不同设备存储空间大小不一致,浏览器的最大存储空间是动态的,它完全取决于你的磁盘大小。全局限制为可用磁盘空间的 50%。在 Firefox 中,一个名为 Quota Manager 的内部浏览器工具会跟踪每个源用尽的磁盘空间,并在必要时删除数据。
因此,如果你的硬盘驱动器是 500GB,那么浏览器的总存储容量为 250GB。如果超过此范围,则会发起称为源回收的过程,删除整个源的数据,直到存储量再次低于限制。删除源数据没有只删一部分的说法——因为这样可能会导致不一致的问题。
另外一个是当存储空间达到上限之后会执行LRU策略,当可用磁盘空间已满时,配额管理器将根据 LRU 策略开始清除数据——最近最少使用的源将首先被删除,然后是下一个,直到浏览器不再超过限制。
异步执行
IndexDB执行的操作都是异步完成的,以免阻塞应用程序。
由于存储不涉及到浏览器的Document操作,因此我们还可以配合Web Worker实现数据交互。把IndexDB的操作都放在Web Worker中,大大提升性能。
使用方法
下面来介绍一下如何在Vue项目中使用IndexDB,我们实现两套方案,第一种是基于Hook模式,第二种是基于Class模式。
这样能够很好的应用到Vue2/3中(React同理)。
基于Hook
使用数据库首先需要进行数据库注册,注册成功之后,才可以进行数据的事务操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
| import { Toast } from "vant";
interface Result { name: string; content: string; }
interface Get { result: Result; onsuccess: () => void; onerror: (reason?: any) => void; }
interface Put { onsuccess: (target: { result: Result }) => void; onerror: (reason?: any) => void; }
const SQL_NAME = "yuyishijia-h5"; const DB_NAME = "h5"; let request: any; let db: any;
function init() { return new Promise((resolve, reject) => { request = window.indexedDB.open(SQL_NAME); request.onerror = (event: any) => { reject(event); Toast("缓存获取失败"); }; request.onsuccess = (event: any) => { resolve(event.target.result); db = event.target.result; }; request.onupgradeneeded = (event: any) => { db = event.target.result; let objectStore; if (!db.objectStoreNames.contains(DB_NAME)) { objectStore = db.createObjectStore(DB_NAME, { keyPath: "name", unique: true, }); objectStore.createIndex("content", "content", { unique: false }); } }; }); }
export async function get(name: string): Promise<Result> { return new Promise<Result>((resolve, reject) => { const select: Get = db .transaction([DB_NAME], "readonly") .objectStore(DB_NAME) .get(name);
select.onsuccess = function () { resolve(select.result); }; select.onerror = reject; }); }
export async function add(name: string, content: string): Promise<Result> { return new Promise<Result>((resolve, reject) => { const select: Put = db .transaction([DB_NAME], "readwrite") .objectStore(DB_NAME) .add({ name: name, content });
select.onsuccess = (event: any) => { resolve(event.target.result); }; select.onerror = reject; }); }
export async function update(name: string, content: string): Promise<Result> { return new Promise((resolve, reject) => { const select: Put = db .transaction([DB_NAME], "readwrite") .objectStore(DB_NAME) .put({ name, content });
select.onsuccess = (event: any) => { resolve(event.target.result); }; select.onerror = reject; }); }
export async function remove(name: string): Promise<Result> { return new Promise((resolve, reject) => { const select: Put = db .transaction([DB_NAME], "readwrite") .objectStore(DB_NAME) .delete(name);
select.onsuccess = (event: any) => { resolve(event.target.result); }; select.onerror = reject; }); }
export default { init, add, get, update, remove, };
|
在使用IndexDB之前需要先在入口函数注册数据库。
1 2 3 4 5 6 7
| import { createApp } from "vue"; import indexDB from "@/utils/storage";
await indexDB.init(); const app = createApp(App); app.mount("#app");
|
注册成功后,可以在业务中对数据库表进行增删改查操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <template> <div>使用IndexDB</div> </template>
<script setup> import IndexDB from "@/utils/storage";
const loadData = async () => { let cache: any; try { cache = await IndexDB.get('主键名称'); } catch (error) { console.log(error); } }; </script>
|
基于Class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
| import { Toast } from "vant";
const SQL_NAME = "yuyishijia-h5"; const DB_NAME = "h5";
class indexDb { name: string; request: any; db: any; constructor(name: string) { this.name = name; }
init() { return new Promise((resolve, reject) => { this.request = window.indexedDB.open(SQL_NAME); this.request.onerror = (event: any) => { reject(event); Toast("缓存获取失败"); }; this.request.onsuccess = (event: any) => { resolve(event.target.result); this.db = event.target.result; }; this.request.onupgradeneeded = (event: any) => { this.db = event.target.result; let objectStore; if (!this.db.objectStoreNames.contains(DB_NAME)) { objectStore = this.db.createObjectStore(DB_NAME, { keyPath: "name", unique: true, }); objectStore.createIndex("content", "content", { unique: false }); } }; }); }
async get(name: string) { return new Promise((resolve, reject) => { const transaction = this.db.transaction([DB_NAME], "readonly"); const objectStore = transaction.objectStore(DB_NAME); const req = objectStore.get(name);
req.onsuccess = function () { resolve(req.result); }; req.onerror = reject; }); }
async add(content: string) { return new Promise((resolve, reject) => { const select = this.db .transaction([DB_NAME], "readwrite") .objectStore(DB_NAME) .add({ name: this.name, content });
select.onsuccess = (event: any) => { resolve(event.target.result); }; select.onerror = reject; }); }
async update(name: string, content: string) { return new Promise((resolve, reject) => { const select = this.db .transaction([DB_NAME], "readwrite") .objectStore(DB_NAME) .put({ name, content });
select.onsuccess = (event: any) => { resolve(event.target.result); }; select.onerror = reject; }); } }
export default indexDb;
|
class模式推荐配合pinia(vuex)做全局数据存储。
1 2 3 4 5 6 7 8 9 10 11
| import { defineStore } from 'pinia' import indexDB from "@/utils/storage";
export const useDbStore = defineStore('db', () => { const db = new indexDB() db.init() return { add: db.add, update: db.update, remove: db.remove, get: db.get } })
|
标签:
JavaScriptIndexedDBVue.js
作者:smallzip
链接:https://juejin.cn/post/7303475295028297767
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。