Web 客户端数据库 IndexedDB 速览及应用

news2024/9/25 7:19:44

#1 概述

IndexedDB 是一种底层 API,用于在客户端存储大量的结构化数据(也包括文件/二进制大型对象,如 blobs)。该 API 使用索引实现对数据的高性能搜索。虽然 Web Storage 在存储较少量的数据很有用,但对于存储更大量的结构化数据来说力不从心。而 IndexedDB 提供了这种场景的解决方案。本页面 MDN IndexedDB 的主要引导页 - 这里,我们提供了完整的 API 参考和使用指南,浏览器支持细节,以及关键概念的一些解释的链接。

截至2023年9月,主流浏览器对 IndexedDB 的支持情况如下(数据来源:caniuse.com):

如果您的应用需要支持 IE 浏览器,抱歉,用不了 IndexedDB 😔。

#1.1 特性

  • 在 Web Worker 中可用
  • 容量足够大
  • 支持事物及索引

#1.2 前端存储方案对比

方式数据生命周期容量大小与后端通信
cookie通常由服务端创建(配置 Response 的 Header),前端也支持手动录入,可设置存活期限4K参与
localStorage创建后一直存在(除非被清理)5M不参与
sessionStorage仅限当前标签页,关闭后丢失(页面刷新可存活)5M不参与
IndexedDB同 localStorage理论上不受限不参与

IndexedDB 容量上限其实受浏览器、操作系统、磁盘空间等因素限制,通常认为 250 M,毕竟一个网页需要处理到过大的数据,怎么看都有点流氓😄

#2 如何使用

#2.1 开源库

目前市面上已经有不少优秀针对 IndexedDB 的库,这里重点介绍localForage、Dexie.js、JsStore

localForage:一个快速且简单的前端存储库,提供极简API(类 localStorage),在不支持 IndexedDB 或 WebSQL 的环境下自动使用 localStorage 👍,业务场景简单下的首选。

Dexie.jsJsStore都是仅针对 IndexedDB 的封装,并提供类 SQL 的接口。

#2.2 简单封装

在不希望引入新库的情况下,可以试试自己封装

/**
 * 封装 indexedDB,默认的数据库为 db
 */
export default class IDB {
    /**
     * @type {IDBDatabase}
     */
    db          = undefined
    /**
     * @type {Object}
     */
    table       = undefined
    /**
     * @type {String}
     */
    name        = window.DBName || "db"

    /**
     * @class IDB
     * @param {String|Object} nameOrObj - 数据表名或者配置对象
     * @param {String} dbName - 数据库名,默认使用全局属性 DBName
     */
    constructor(nameOrObj, dbName){
        this.table = typeof(nameOrObj) === 'string'?{name: nameOrObj, options:{keyPath:"id"}} : nameOrObj
        if(dbName)  this.name = dbName
    }

    /**
     * 初始化数据库连接
     * @returns {Promise}
     */
    #init = ()=> new Promise((ok, fail)=>{
        if(!!this.db) return ok(this.db)

        const req = indexedDB.open(this.name)
        req.onsuccess = e=> {
            this.db = e.target.result
            ok(this.db)
        }
        req.onerror = e=>fail(e)
        req.onupgradeneeded = e=>{
            this.db = e.target.result

            let { name, options } = this.table
            if(!this.db.objectStoreNames.contains(name)){
                this.db.createObjectStore(name, options)
            }
        }
    })

    /**
     * 单条或批量插入数据行,返回 {count: 处理数据量, used:耗时(单位ms)}
     * @param {Object|Array} rows - 待插入的数据对象或者数组
     * @returns {Promise}
     */
    insert = rows => new Promise((ok, fail)=>this.#init().then(async db=>{
        let { name } = this.table
        let store = db.transaction(name, 'readwrite').objectStore(name)
        rows = Array.isArray(rows)? rows : [rows]

        let started = Date.now()
        let count = 0
        for (const row of rows) {
            try{
                await store.put(row)
                count ++
            }catch(e){
                return fail(e)
            }
        }
        ok({ count, used: Date.now()-started })
    }))

    /**
     * 按主键读取数据行
     * @param {String} key - 数据行主键
     * @returns {Promise}
     */
    get = key => new Promise(async(ok, fail)=> this.#init().then(db=>{
        let { name } = this.table
        const req = db.transaction(name, 'readonly').objectStore(name).get(key)
        req.onsuccess = e=> ok(req.result)
        req.onerror = ({target})=>{
            fail(target.error)
        }
    }))

    /**
     *
     * @param {String} key - 数据行主键
     * @returns {Promise}
     */
    remove = key => new Promise((ok, fail)=> this.#init().then(db=>{
        let { name } = this.table

        const req = db.transaction(name, 'readwrite').objectStore(name).delete(key)
        req.onsuccess = e=> ok()
        req.onerror = ({target})=>{
            fail(target.error)
        }
    }))

    /**
     * 按主键更新数据行(可新增字段)
     * @param {String} key - 数据行主键
     * @param {Object} data - 待更新的字段
     * @returns {Promise}
     */
    update = (key, data)=> new Promise((ok, fail)=> this.#init().then(db=>{
        this.get(key).then(row=>{
            if(row==undefined)  fail(`KEY=${key}的数据对象不存在`)

            let { name } = this.table

            const req = db.transaction(name, 'readwrite').objectStore(name).put(Object.assign(row, data))
            req.onsuccess = e=> ok(req.result)
            req.onerror = ({target})=>{
                fail(target.error)
            }
        })
    }))

    /**
     * 游标方式遍历数据表,返回 {count:处理数据量, used:耗时(单位ms)}
     * @param {Function} worker - 处理函数
     * @param {IDBKeyRange} range - 查询条件,详见 https://developer.mozilla.org/en-US/docs/Web/API/IDBKeyRange
     * @returns {Promise}
     */
    stream = (worker, range)=> new Promise((ok, fail)=> this.#init().then(db=>{
        let { name } = this.table
        let count = 0
        let started = Date.now()
        const req = db.transaction(name, 'readonly').objectStore(name).openCursor(range)
        req.onsuccess = e=> {
            let cursor = e.target.result
            if(cursor){
                worker(cursor.value)
                cursor.continue()
                count ++
            }
            else
                ok({count, used: Date.now()-started})
        }
        req.onerror = ({target})=>{
            fail(target.error)
        }
    }))

    close = ()=> {
        if(!!this.db) {
            this.db.close()
            this.db = null
        }
    }
}

#2.3 使用说明

// 引入(上述类保存到 idb.js )
import IDB from "./idb.js"

// 创建名为 test 的表
const db - new IDB("user")
// 插入两条数据,注意:主键属性(默认 id)为必填项,重复主键时会覆盖旧数据
db.insert([
	{ id:1, name:"集成显卡", vip:1 },
	{ id:2, name:"张三", vip:0 }
])
// 查询ID=1的数据
db.get(1).then(row=>console.debug(row))
// 遍历数据
db.stream(row=>console.debug(row)).then(result=>console.debug(`遍历完成`, result))
// 关闭连接
db.close()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1079199.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

QTableWidget 表格增删数据

QTableWidgetQTableWidgetQTableWidget部分使用方法,如在表格中插入或删除一行数据以及清空表格数据等。在添加数据时,设置了条件判断如正则表达式,若用户输入的数据不合法,则添加失败并提示用户错误的地方,便于用户修…

python结合excel数据轻松实现接口自动化测试

在刚刚进入测试行业的时候,最开始也是做功能测试,我想很多伙伴和我一样,觉得自动化测试都很高端,很神秘。迫不及待的想去学习作自动化测试。 以前比较常用数据库python做自动化,后面发现excel个人觉得更加适合&#x…

【java学习】面向对象编程(12)

文章目录 前言1. 什么是"面向对象"的编程思想?1.1. 面向对象与面向过程 2. java类及类成员 前言 学习路线: 学习面向对象内容的三条主线1. java类及类成员2. 面向对象的三大特征3. 其他关键字 学习内容: 要学习的内容1. 面向对象与面向过…

内网渗透——黄金票据与白银票据

文章目录 黄金票据与白银票据1. 背景2. 具体实现2.1 Kerberos协议认证流程 3. 黄金票据3.1 条件3.2 适用场景3.3 利用方式 4. 白银票据4.1 条件4.2 适用场景4.3 利用方式 5. 金票和银票的区别5.1 获取的权限不同5.2 认证流程不同5.3 加密方式不同 6. 经典面试题6.1 什么是黄金票…

(蓝宝书)网络安全——CTF那些事儿

欢迎关注博主 Mindtechnist 或加入【智能科技社区】一起学习和分享Linux、C、C、Python、Matlab,机器人运动控制、多机器人协作,智能优化算法,滤波估计、多传感器信息融合,机器学习,人工智能等相关领域的知识和技术。关…

【常用页面记录】vue+elementUI实现搜索框+上拉加载列表

一、代码 <template><div class"mainBox"><div class"headbox"><el-input placeholder"请输入文件名称搜索" prefix-icon"el-icon-search" v-model"fileName" :clearable"true" change&qu…

Spring5应用之事务处理

作者简介&#xff1a;☕️大家好&#xff0c;我是Aomsir&#xff0c;一个爱折腾的开发者&#xff01; 个人主页&#xff1a;Aomsir_Spring5应用专栏,Netty应用专栏,RPC应用专栏-CSDN博客 当前专栏&#xff1a;Spring5应用专栏_Aomsir的博客-CSDN博客 文章目录 参考文献前言事务…

【Vuex+ElementUI】

一、导言 1、引言 Vuex是一个用于Vue.js应用程序的状态管理模式和库。它建立在Vue.js的响应式系统之上&#xff0c;提供了一种集中管理应用程序状态的方式。使用Vuex&#xff0c;您可以将应用程序的状态存储在一个单一的位置&#xff08;即“存储”&#xff09;中&#xff0c;…

软件‘小程序‘前台开发软件定制的知识|app网站搭建

软件&#xff0c;小程序&#xff0c;前台开发软件定制的知识 随着互联网的快速发展&#xff0c;软件&#xff0c;小程序&#xff0c;前台开发软件定制已经成为了企业必备的工具。它可以帮助企业更好地管理业务&#xff0c;提高效率&#xff0c;增强用户体验。那么&#xff0c;什…

英语——分享篇——每日100词——701-800

audience——n.听众&#xff0c;观众&#xff0c;读者——au澳大利亚(编码)di弟(拼音)ence摁厕(拼音)———听众在澳大利亚被弟弟摁倒在厕所 flu———n.流行性感冒———flu俘虏————俘虏带来流行性感冒 belt———n.腰带&#xff0c;皮带———b6(象形)el饿狼(拼音)t伞(…

flutter入门实践2——将完成的flutter软件打包为apk

将完成的flutter软件打包为apk&#xff1a; 视频版&#xff1a; 【前端教程-Flutter篇-flutter之打包安卓版本的APP】https://www.bilibili.com/video/BV11K4y1S7Sg?vd_sourcec008a8e3bd95154e374408adc754394a 文字版&#xff1a;Flutter 打包APP &#xff08;Android &am…

《动手学深度学习 Pytorch版》 8.3 语言模型和数据集

8.3.1 学习语言模型 依靠在 8.1 节中对序列模型的分析&#xff0c;可以在单词级别对文本数据进行词元化。基本概率规则如下&#xff1a; P ( x 1 , x 2 , … , x T ) ∏ t 1 T P ( x t ∣ x 1 , … , x t − 1 ) P(x_1,x_2,\dots,x_T)\prod^T_{t1}P(x_t|x_1,\dots,x_{t-1}) …

黑马JVM总结(三十)

&#xff08;1&#xff09;类加载-连接-解析 new对象&#xff0c;会触发对象的解析和初始化 通过类加载器加载类C&#xff0c;这里只会进行类C的加载并不会导致类C的解析以及初始化&#xff0c;这样加载类C的时候类D也不会解析跟初始化 通过jps找到进程id&#xff1a; 打开…

Ant Design Form.List基础用法

使用 Form.List 使用 项目中需要在新增可以多个如图 代码如下 // An highlighted block <Card title"产品信息" bordered{false}><Form.List name"productList" >{(fields, {add, remove}) > (<>{fields.map((field) > (<Ro…

1.2Python 三方库的安装以 pandas 为例_python量化实用版教程(初级)

Python 三方库的安装以 pandas 为例 Python 拥有丰富的第三方库&#xff0c;可以方便地进行各种编程任务。以 pandas 库为例&#xff0c;下面是安装 pandas 库的步骤&#xff1a; 1. 打开命令行终端&#xff08;Windows 用户可以使用 cmd&#xff0c;Linux 和 Ma…

ArcGIS/GeoScene脚本:基于粒子群优化的支持向量机分类模型

参数输入 输出 栅格 预测为负类的概率 预测为正类的概率 二值化结果 评估结果 ROC曲线

绝地求生是一款大逃杀游戏,玩家需要在一个封闭的地图上与其他玩家进行生存竞争

绝地求生是韩国蓝洞公司开发的一款大逃杀游戏。玩家绝地求生是一款以生存竞争为主题的多人在线射击游戏。玩家将被放置在一个封闭的地图中&#xff0c;需要在资源有限的环境中生存下来&#xff0c;并与其他玩家进行战斗。玩家需要不断搜索武器和装备&#xff0c;同时要注意限定…

web:[MRCTF2020]你传你呢

题目 点进页面显示如下 上传文件&#xff0c;先随便上传一个文件看看情况 构造含有一句话木马的图片上传 访问显示错误 这里参考了大佬的wp&#xff0c;上传一个.htaccess文件,这个.htaccess文件的作用就是把这个图片文件解析成php代码执行 .htaccess文件的内容为 <FilesM…

OpenCV4(C++)—— 几何图形的绘制

文章目录 一、基本图形1、线2、线圆3、线椭圆4、矩形 二、多边形 一、基本图形 1、线 绘制线&#xff0c;要给出两个点坐标 void cv::line(InputOutputArray img, Point pt1, Point pt2, const Scalar& color, int thickness 1, int lineType LINE_8, int shift 0);…

【编程的黄金法则】适度注释,保持平衡

引言 身为一名程序员&#xff0c;你是否也曾为到底需不需要写注释而烦恼&#xff1f; 有些人认为&#xff0c;代码如诗&#xff0c;注释则是诗中的注解&#xff0c;能够帮助他人&#xff08;包括未来的你自己&#xff09;理解代码的意图。而另一些人则认为&#xff0c;好的代…