玩转IndexedDB,比localStorage、cookie还要强大的网页端本地缓存

news2024/10/7 11:27:22


随着浏览器的功能不断增强,越来越多的网站开始考虑,将大量数据储存在客户端,这样可以减少从服务器获取数据,直接从本地获取数据。

现有的浏览器数据储存方案,都不适合储存大量数据:Cookie 的大小不超过 4KB,且每次请求都会发送回服务器;LocalStorage 在 2.5MB 到 10MB 之间(各家浏览器不同),而且不提供搜索功能,不能建立自定义的索引。所以,需要一种新的解决方案,这就是 IndexedDB 诞生的背景。

通俗地说,IndexedDB 就是浏览器提供的本地数据库,它可以被网页脚本创建和操作。IndexedDB 允许储存大量数据,提供查找接口,还能建立索引。这些都是 LocalStorage 所不具备的。就数据库类型而言,IndexedDB 不属于关系型数据库(不支持 SQL 查询语句),更接近 NoSQL 数据库。

对比cookielocalStoragesessionStorageindexedDB
存储大小4kb5M5M很多于 250MB,甚至没有上限
与服务器端通讯每次都会携带在HTTP头中,若是使用cookie保存过多数据会带来性能问题仅在客户端(即浏览器)中保存,不参与和服务器的通讯
生命周期通常由服务器生成,可设置失效时间。若是在浏览器端生成Cookie,默认是关闭浏览器后失效除非被清除,不然永久保存仅在当前会话下有效,关闭页面或浏览器后被清除除非被清除,不然永久保存
使用场景判断用户是否登陆存储一些内容稳定的资源。好比图片内容丰富的电商网站会用它来存储 Base64 格式的图片字符串存储一些当前会话的信息,好比微博的 sessionStorage就主要是存储你本次会话的浏览足迹和 localStorage 用途相似:
1.  存储量会更大
2. localStorage使用简单字符串键值对在本地存储数据,而indexedDB能够存储任意类型的值(适合键值对较多的数据,若是使用 localStorage 存储每次都要写入,写出须要字符串化和对象化)
复制代码


目前,Chrome 27+、Firefox 21+、Opera 15+和IE 10+支持这个API,但是Safari完全不支持。

下面的代码用来检查浏览器是否支持这个API。

if("indexedDB" in window) {
    // 支持
} else {
    // 不支持
}

IndexedDB 具有以下特点。

(1)键值对储存。 IndexedDB 内部采用对象仓库(object store)存放数据。所有类型的数据都可以直接存入,包括 JavaScript 对象。对象仓库中,数据以“键值对”的形式保存,每一个数据记录都有对应的主键,主键是独一无二的,不能有重复,否则会抛出一个错误。

(2)异步。 IndexedDB 操作时不会锁死浏览器,用户依然可以进行其他操作,这与 LocalStorage 形成对比,后者的操作是同步的。异步设计是为了防止大量数据的读写,拖慢网页的表现。

(3)支持事务。 IndexedDB 支持事务(transaction),这意味着一系列操作步骤之中,只要有一步失败,整个事务就都取消,数据库回滚到事务发生之前的状态,不存在只改写一部分数据的情况。

(4)同源限制。 IndexedDB 受到同源限制,每一个数据库对应创建它的域名。网页只能访问自身域名下的数据库,而不能访问跨域的数据库。

(5)储存空间大。 IndexedDB 的储存空间比 LocalStorage 大得多,一般来说不少于 250MB,甚至没有上限。

(6)支持二进制储存。 IndexedDB 不仅可以储存字符串,还可以储存二进制数据(ArrayBuffer 对象和 Blob 对象)。

IndexedDB 是一个比较复杂的 API,涉及不少概念。它把不同的实体,抽象成一个个对象接口。学习这个 API,就是学习它的各种对象接口。

  • 数据库:IDBDatabase 对象
  • 对象仓库:IDBObjectStore 对象
  • 索引: IDBIndex 对象
  • 事务: IDBTransaction 对象
  • 操作请求:IDBRequest 对象
  • 指针: IDBCursor 对象
  • 主键集合:IDBKeyRange 对象

下面是一些主要的概念。

(1)数据库

数据库是一系列相关数据的容器。每个域名(严格的说,是协议 + 域名 + 端口)都可以新建任意多个数据库。

IndexedDB 数据库有版本的概念。同一个时刻,只能有一个版本的数据库存在。如果要修改数据库结构(新增或删除表、索引或者主键),只能通过升级数据库版本完成。

(2)对象仓库

每个数据库包含若干个对象仓库(object store)。它类似于关系型数据库的表格。

(3)数据记录

对象仓库保存的是数据记录。每条记录类似于关系型数据库的行,但是只有主键和数据体两部分。主键用来建立默认的索引,必须是不同的,否则会报错。主键可以是数据记录里面的一个属性,也可以指定为一个递增的整数编号。

{ id: 1, value: '对应的值' }

上面的对象中,id属性可以当作主键。

数据体可以是任意数据类型,不限于对象。

(4)索引

为了加速数据的检索,可以在对象仓库里面,为不同的属性建立索引。

(5)事务

数据记录的读写和删改,都要通过事务完成。事务对象提供errorabortcomplete三个事件,用来监听操作结果。


用例代码 

<template>
    <div>
        <el-input v-model.trim="databaseName" :placeholder="`请输入数据库名称`" />
        <el-input v-model.trim="tableName" :placeholder="`请输入表名称`" />
        <el-input v-model.trim="index" :placeholder="`请输入字段名`" />
        <el-button type="primary" @click="btn1()">创建数据库</el-button>

        <hr>

        <el-input v-model.trim="value" :placeholder="`请输入值`" />
        <el-button type="success" @click="btn2()">添加数据</el-button>

        <hr>

        <el-input v-model.trim="keyPathValue" :placeholder="`请输入主键值`" />
        <el-input v-model.trim="newValue" :placeholder="`请输入修改值`" />
        <el-button type="warning" @click="btn3()">修改数据</el-button>

        <hr>

        <el-button type="info" @click="btn4()">读取数据</el-button>
        <template v-if="tableData.length">
            <el-button type="danger" @click="btn5">删除全部</el-button>
            <el-table :data="tableData">
                <el-table-column :prop="keyPath" :label="keyPath" />
                <el-table-column :prop="index" :label="index" />
                <el-table-column label="操作">
                    <template slot-scope="scope">
                        <el-button size="mini" type="danger" @click.stop="btn6(scope.row[keyPath])">删除</el-button>
                    </template>
                </el-table-column>
            </el-table>
        </template>
    </div>
</template>
<script>
export default {
    data() {
        return {
            databaseName: '',
            tableName: '',
            keyPath: 'id',
            keyPathValue: '',
            index: '',
            value: '',
            newValue: '',
            tableData: [],
        }
    },
    methods: {
        // 用例----------------------------------------
        // 创建
        btn1() {
            let databaseName = this.databaseName;
            let version = this.version;
            let tableName = this.tableName;
            let keyPath = this.keyPath;
            let indexs = [
                [this.index, this.index, { unique: false }],
            ];//如需定义多格字段,就多几个数组
            // 创建表
            this.creatDatabaseTable({
                databaseName, version,
                tableName,//定义表名
                keyPath,//定义主键
                indexs,// 定义索引字段
            });
        },
        // 添加
        btn2() {
            let databaseName = this.databaseName;
            let version = this.version;
            let tableName = this.tableName;
            let keyPath = this.keyPath;
            let index = this.index;
            this.addData({
                databaseName, version, tableName,
                data: {
                    [keyPath]: '********'.replace(/\*/g, () => Math.round(Math.random() * 15).toString(16)),//随机id
                    [index]: this.value,
                },
                onsuccess: d => { console.log(`onsuccess`, d); },
                onerror: d => { console.log(`onerror`, d); },
            })
        },
        // 修改
        btn3() {
            let databaseName = this.databaseName;
            let version = this.version;
            let tableName = this.tableName;
            let keyPath = this.keyPath;
            let index = this.index;
            this.updateData({
                databaseName, version, tableName,
                data: {
                    [keyPath]: this.keyPathValue,
                    [index]: this.newValue,
                }
            });
            this.btn4();//刷新数据
        },
        // 读取
        btn4() {
            let databaseName = this.databaseName;
            let version = this.version;
            let tableName = this.tableName;
            this.readData({
                databaseName, version, tableName,
                onsuccess: ({ data }) => { this.tableData = data },
            })
        },
        // 删除全部
        btn5() {
            let databaseName = this.databaseName;
            let version = this.version;
            let tableName = this.tableName;
            this.delAllData({ databaseName, version, tableName, });
            this.btn4();//刷新数据
        },
        // 删除
        btn6(id) {
            let databaseName = this.databaseName;
            let version = this.version;
            let tableName = this.tableName;
            this.delData({
                databaseName, version, tableName,
                data: id,//需要删除的数据主键
                onsuccess: d => {
                    this.btn4();//刷新数据
                },
                onerror: d => { console.log(`onerror`, d); },
            })
        },
        // indexedDB----------------------------------------
        // 1、创建or打开客户端数据库
        createDatabase({ databaseName, version = 1, onupgradeneeded, onsuccess, onerror } = {}) {
            let request = window.indexedDB.open(databaseName, version);
            request.onupgradeneeded = onupgradeneeded;
            request.onsuccess = onsuccess;
            request.onerror = onerror;
        },
        getDatabase(obj) { return this.createDatabase(obj); },//获取数据库
        // 2、创建表
        creatDatabaseTable({ databaseName, version, tableName, keyPath, indexs, onupgradeneeded, onsuccess, onerror } = {}) {
            this.getDatabase({
                databaseName, version,
                onupgradeneeded: d => {
                    let database = d.target.result;
                    if (!database.objectStoreNames.contains(tableName)) {
                        //createObjectStore只能在onupgradeneeded里面执行
                        let objectStore = database.createObjectStore(tableName, { keyPath });
                        (indexs || []).forEach(v => objectStore.createIndex(...v));
                        onupgradeneeded && onupgradeneeded({ event: d, objectStore });
                    }
                },
                onsuccess,
                onerror,
            })
        },
        // 3、添加数据or修改数据or删除数据
        addData({ databaseName, version, tableName, data, onsuccess, onerror, triggerName = 'add' } = {}) {
            this.getDatabase({
                databaseName, version,
                onsuccess: d => {
                    let database = d.target.result;
                    let objectStore = database.transaction(tableName, 'readwrite').objectStore(tableName);
                    let request_objectStore = objectStore.get(data[objectStore.keyPath]);
                    request_objectStore.onsuccess = event => {
                        event.target.result && (triggerName = 'put');//如果已经存在该主键数据,就变成修改
                        let request = objectStore[triggerName](data);
                        request.onsuccess = onsuccess;
                        request.onerror = onerror;
                    };
                },
            })
        },
        // 4、修改数据
        updateData(obj) { this.addData({ ...obj, triggerName: 'put' }) },
        // 5、删除数据
        delData(obj) { this.addData({ ...obj, triggerName: 'delete' }) },
        delAllData({ databaseName, version, tableName } = {}) {
            this.getDatabase({
                databaseName, version,
                onsuccess: d => {
                    let objectStore = d.target.result.transaction(tableName, 'readwrite').objectStore(tableName);
                    objectStore.clear();
                },
            })
        },
        // 6、读取数据
        readData({ databaseName, version, tableName, onsuccess } = {}) {
            this.getDatabase({
                databaseName, version,
                onsuccess: d => {
                    let database = d.target.result;
                    if (database.objectStoreNames.contains(tableName)) {
                        let objectStore = database.transaction(tableName).objectStore(tableName);
                        let data = [];
                        objectStore.openCursor().onsuccess = event => {
                            let cursor = event.target.result;
                            if (cursor) {
                                data.push(cursor.value); cursor.continue();
                            } else {
                                onsuccess && onsuccess({ event, data });
                                // console.log('没有更多数据了!'); 
                            }
                        };
                    } else onsuccess && onsuccess({ msg: '表格不存在!', data: [] });
                },
            })
        },
        // ----------------------------------------
    }
};
</script>

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

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

相关文章

回归预测 | MATLAB实现基于SAE堆叠自编辑器多输入单输出回归预测

回归预测 | MATLAB实现基于SAE堆叠自编辑器多输入单输出回归预测 目录 回归预测 | MATLAB实现基于SAE堆叠自编辑器多输入单输出回归预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 1.MATLAB实现基于SAE堆叠自编辑器多输入单输出回归预测&#xff1b; 2.运行环…

找到链表的第一个入环节点

1.题目 给定一个链表的头节点 head &#xff0c;返回链表开始入环的第一个节点。 如果链表无环&#xff0c;则返回 null。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统…

会玩这 10 个 Linux 命令,一定是个有趣的 IT 男!

Linux当中有很多比较有趣的命令&#xff0c;可以动手看看&#xff0c;很简单的。 1.rev命令 一行接一行地颠倒所输入的字符串。 运行&#xff1a; $rev如输入&#xff1a;shiyanlou shiyanlou2.asciiview命令 1.先安装aview $sudo apt-get install aview2.再安装imagema…

zabbix案例--zabbix监控Tomcat

目录 一、 部署tomcat 二、配置zabbix-java-gateway 三、配置zabbix-server 四、配置zabbix-web界面 一、 部署tomcat tar xf apache-tomcat-8.5.16.tar.gz -C /usr/local/ ln -sv /usr/local/apache-tomcat-8.5.16/ /usr/local/tomcat cd /usr/local/tomcat/bin开启JMX…

如何解决docker中出现的“bash: vim: command not found”

目录 问题描述&#xff1a; 问题解决&#xff1a; 问题描述&#xff1a; 在docker中&#xff0c;想要执行vim编辑文件&#xff0c;弹出“docker bash: vim: command not found“&#xff08;如下图&#xff09;&#xff0c;请问该如何解决&#xff1f; 问题解决&#xff1a; …

使用维纳过滤器消除驾驶舱噪音(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

管理类联考——逻辑——论证逻辑——汇总篇——因果推理

因果推理的逻辑方法&#xff08;穆勒五法) 确定现象之间因果关系的方法有五种&#xff1a; 求同法、求异法、求同求异并用法、共变法、剩余法。这五种方法统称为穆勒五法。用穆勒五法确定的因果关系具有或然性。 PS&#xff1a;求同球童&#xff1b;求异球衣&#xff0c;求同…

设计模式——建造者(Builder)模式

建造者模式&#xff08;Builder Pattern&#xff09;&#xff0c;又叫生成器模式&#xff0c;是一种对象构建模式 它可以将复杂对象的建造过程抽象出来&#xff0c;使这个抽象过程的不同实现方法可以构造出不同表现的对象。建造者模式是一步一步创建一个复杂的对象&#xff0c;…

YARN框架和其工作原理流程介绍

目录 一、YARN简介 二、YARN的由来 三、YARN的基本设计思想 四、YARN 的基本架构 4.1 基本架构图 4.2 基本组件介绍 4.2.1 ResourceManager 4.2.1.1 任务调度器(Resource Scheduler) 4.2.1.2 应用程序管理器&#xff08;Applications Manager&#xff09; 4.2.1.3 其他…

官方已经宣布了,2023年下半年软考各科实行机考

软考办官方通知 我认为&#xff0c;第一次参加软考机考的考生会有好处&#xff0c;因为新政策在第一年执行时一般不会太难。我非常好奇如何在论文中画图。 下面&#xff0c;我将从一个曾参加软考高级水平考试的人的角度&#xff0c;讲述机考的优缺点&#xff0c;并分享我自己的…

xxljob搭建(内网穿透)

调度中心搭建 先从码云或者github上将项目拷贝到本地&#xff0c;选择最新的release分支拷贝下来的xxl-job-admin模块就是调度中心&#xff0c;我们需要做的有两点&#xff0c;第一点将doc/db/tables_xxl_job.sql执行&#xff0c;第二点修改xxl-job-admin的application.proper…

恒运资本:算力股爆发,地产股全线下挫,海外机构调研股出炉

60股近期获海外组织调研&#xff0c;医疗龙头最受组织重视。 今日早盘三大指数全线低开&#xff0c;延续调整走势&#xff0c;上证指数跌1.01%&#xff0c;深证成指跌1.35%&#xff0c;创业板指跌1.6%。AI概念股逆市走强&#xff0c;算力、数据要素等方向领涨&#xff0c;朗威股…

从小白到大神之路之学习运维第79天-------Kubernetes网络组件详解

第四阶段 时 间&#xff1a;2023年8月14日 参加人&#xff1a;全班人员 内 容&#xff1a; Kubernetes网络组件详解 目录 一、Kubernetes网络组件 &#xff08;一&#xff09;Flannel网络组件 &#xff08;二&#xff09;Calico 网络插件 &#xff08;1&#xff09;…

U盘数据如何保密?U盘数据加密软件合集

当我们将重要数据存放在U盘中时&#xff0c;必须要保障U盘的安全&#xff0c;以避免数据泄露。那么&#xff0c;U盘数据该如何保密呢&#xff1f;下面我们就来盘点一下那些好用的U盘数据保护方法。 U盘写保护 起初U盘写保护是专门为U盘防病毒而设计的&#xff0c;写保护后会将…

mysql滑动窗口案例

获取学科最高分 SELECT DISTINCT name,subject,MAX(score) OVER (PARTITION by subject) as 此学科最高分数 from scores;获取学科的报名人数 select DISTINCT subject,count(name) over (partition by subject) as 报名此学科的人数 from scores; 求学科总分 SELECT DISTI…

【C++】多态的概念和简单介绍、虚函数、虚函数重写、多态构成的条件、重载、重写、重定义

文章目录 多态1.多态的概念和介绍2.虚函数2.1final2.2override 3.虚函数的重写3.1协变3.2析构函数的重写 4.多态构成的条件5.重载、重写、重定义...... 多态 1.多态的概念和介绍 C中的多态是一种面向对象编程的特性&#xff0c;它允许不同的对象对同一个消息做出不同的响应。 …

1475.商品折扣后的最终价格

文章目录 题目描述解题思路&#xff1a;方法一&#xff1a;通俗解法方法二&#xff1a;单调栈 leetcode原题链接 1475. 商品折扣后的最终价格 题目描述 给你一个数组 prices &#xff0c;其中 prices[i] 是商店里第 i 件商品的价格。 商店里正在进行促销活动&#xff0c;如果你…

linux安装oracle11g

linux安装oracle11g 环境&#xff1a; redhat7 版本 11.2.0.4 一、部署环境准备 1.1 关闭selinux [rootlocalhost software]# vi /etc/selinux/config # This file controls the state of SELinux on the system. # SELINUX can take one of these three values: # enforci…

JavaFx基础学习【五】:FXML布局文件使用

一、介绍 FXML是一种在JavaFX应用程序中定义用户界面的&#xff0c;基于XML的声明性标记语言。FXML非常适用来静态布局&#xff0c;如表单、控件和表格。 如果你还不是不明白FXML到底是什么&#xff0c;你可以类比HTML&#xff0c;我们可以通过HTML来做web页面的UI&#xff0…

全文检索与日志管理 Elasticsearch(上)

一、Elasticsearch介绍 1.1 全文检索索引 Elasticsearch是一个全文检索服务器&#xff0c;全文检索是一种非结构化数据的搜索方式。 那么什么是结构化数据和非结构化数据呢&#xff1f; 结构化数据&#xff1a;指具有固定格式固定长度的数据&#xff0c;如数据库中的字段。 …