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


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

现有的浏览器数据储存方案,都不适合储存大量数据: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 } = {}) {
            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 });
                    }
                },
            })
        },
        // 3、添加数据or修改数据or删除数据
        addData({ databaseName, version, tableName, data, onsuccess, onerror, triggerName = 'add' } = {}) {
            this.getDatabase({
                databaseName, version,
                onsuccess: d => {
                    let database = d.target.result;
                    let request = database.transaction(tableName, 'readwrite').objectStore(tableName)[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>

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

安科瑞电力监控系统在某区块页岩气地面集输工程中的应用

摘要&#xff1a;Acrel-2000Z电力监控系统适用于35kV及以下电压等级的各类变电站&#xff0c;可以帮助用户掌握配电系统实时运行状态&#xff0c; 获取预警、告警等各类事件&#xff0c;实现区域的无人值守&#xff0c;提高监管水平。本文介绍了安科瑞电力监控系统Acrel-2000在…

QEMU源码全解析37 —— Machine(7)

接前一篇文章&#xff1a;QEMU源码全解析36 —— Machine&#xff08;6&#xff09; 本文内容参考&#xff1a; 《趣谈Linux操作系统》 —— 刘超&#xff0c;极客时间 《QEMU/KVM》源码解析与应用 —— 李强&#xff0c;机械工业出版社 特此致谢&#xff01; 上回书讲完了q…

java 加载商户API私钥 (pem证书私钥)

1. pem证书放在resources目录下 2. 加载证书的工具类 import com.wechat.pay.contrib.apache.httpclient.util.PemUtil; // 商户API私钥 (把证书放在项目路径下, 然后加载出来), 加载证书的工具类PrivateKey merchantPrivateKey PemUtil.loadPrivateKey(new FileInp…

使用FTP文件传输协议的潜在风险

数据&#xff08;事实&#xff0c;数字&#xff0c;价值&#xff09;是当今业务运行的核心要素。但是&#xff0c;如果数据没有得到有效的存储和传输&#xff0c;它们就会成为阻碍业务发展的障碍。如果企业不能及时地把数据送到合适的地方&#xff0c;就会造成严重的经济损失。…

8.10 用redis实现缓存功能和Spring Cache

什么是缓存? 缓存(Cache), 就是数据交换的缓冲区,俗称的缓存就是缓冲区内的数据,一般从数据库中获取,存储于本地代码。 通过Redis来缓存数据&#xff0c;减少数据库查询操作; 逻辑 每个分类的菜品保存一份缓存数据 数据库菜品数据有变更时清理缓存数据 如何将商品数据缓存起…

Word 2019打开.doc文档后图片和公式不显示(呈现为白框)的解决办法

Word 2019打开.doc文档后图片和公式不显示&#xff08;呈现为白框&#xff09;的解决办法 目录 Word 2019打开.doc文档后图片和公式不显示&#xff08;呈现为白框&#xff09;的解决办法一、问题描述二、解决方法1.打开 WORD 2019&#xff0c;点击菜单中的“文件”&#xff1b;…

买前必看!蓝牙耳机哪些牌子值得买?蓝牙耳机热销排行榜

蓝牙耳机作为现代生活必备的电子产品之一&#xff0c;我们在选购时的选择就显得尤为重要。随着各大科技公司对蓝牙耳机功能的不断完善&#xff0c;用户对于耳机的期望也越来越高&#xff0c;音质、性能、降噪、舒适度等方面都成为了用户选择蓝牙耳机时考虑的因素。接下来我们一…

Redis复制

在Redis中&#xff0c;用户可以通过执行SLAVEOF命令或者设置slaveof选项&#xff0c;让一个服务器去复制(replicate) 另一个服务器&#xff0c;我们称呼被复制的服务器为主服务器(master)&#xff0c;而对主服务器进行复制的服务器则被称为从服务器(slave)&#xff0c;如下图所…

网络协议栈-基础知识

1、分层模型 1.1、OSI七层模型 1、OSI&#xff08;Open System Interconnection&#xff0c;开放系统互连&#xff09;七层网络模型称为开放式系统互联参考模型 &#xff0c;是一个逻辑上的定义&#xff0c;一个规范&#xff0c;它把网络从逻辑上分为了7层。 2、每一层都有相关…

工厂方法模式-java实现

介绍 工厂方法模式&#xff0c;通过把工厂抽象为一个接口&#xff0c;这样当我们新增具体产品的时候&#xff0c;就只需要实现一个新的具体工厂类即可。一个具体工厂类&#xff0c;对应着一个产品。 请注意&#xff1a;在工厂方法模式中&#xff0c;一个具体工厂类只对应生产…

FPGA + WS2812采灯控制

文章目录 一、WS2812C-2020-V11、产品概述2、引出端排列及功能3、数据传输时间4、数据传输方法 二、使用WS2812C显示图片1、静态显示2、动态显示 一、WS2812C-2020-V1 1、产品概述 WS2812C-2020-V1是一个集控制电路与发光电路于一体的智能外控LED光源&#xff1b;其外型采用最…

谷粒商城第十一天-完善商品分组(主要添上关联属性)

目录 一、总述 二、前端部分 2.1 改良前端获取分组列表接口及其调用 2.2 添加关联的一整套逻辑 三、后端部分 四、总结 一、总述 前端部分和之前的商品品牌添加分类差不多。 也是修改一下前端的分页获取列表的接口&#xff0c;还有就是加上关联的那一套逻辑&#xff0c;…

7.3 详解NiN模型--首次使用多层感知机(1x1卷积核)替换掉全连接层的模型

一.前提知识 多层感知机&#xff1a;由一个输入层&#xff0c;一个或多个隐藏层和一个输出层组成。&#xff08;至少有一个隐藏层&#xff0c;即至少3层&#xff09; 全连接层&#xff1a;是MLP的一种特殊情况&#xff0c;每个节点都与前一层的所有节点连接&#xff0c;全连接…

7.5.tensorRT高级(2)-RAII接口模式下的生产者消费者多batch实现

目录 前言1. RAII接口模式封装生产者消费者2. 问答环节总结 前言 杜老师推出的 tensorRT从零起步高性能部署 课程&#xff0c;之前有看过一遍&#xff0c;但是没有做笔记&#xff0c;很多东西也忘了。这次重新撸一遍&#xff0c;顺便记记笔记。 本次课程学习 tensorRT 高级-RAI…

前后端分离------后端创建笔记(05)用户列表查询接口(上)

本文章转载于【SpringBootVue】全网最简单但实用的前后端分离项目实战笔记 - 前端_大菜007的博客-CSDN博客 仅用于学习和讨论&#xff0c;如有侵权请联系 源码&#xff1a;https://gitee.com/green_vegetables/x-admin-project.git 素材&#xff1a;https://pan.baidu.com/s/…

City Walk带动茶饮品牌售1200万,媒介盒子带你探究奥秘

年轻人生活趋势又出现了一个新鲜词——City Walk&#xff0c;简单来说&#xff0c;City Walk就是没有目的地&#xff0c;没有目标&#xff0c;只是出行&#xff0c;填充自己的生活。 其实这个词源于gap year&#xff0c;而这个说法一直是国外的一些毕业生&#xff0c;大多会在…

plt取消坐标轴刻度、自定义取消绘图边框(或坐标轴)、白边处理、自定义颜色图谱、设置坐标轴刻度朝向

目录 1、取消坐标轴刻度 2、自定义取消绘图边框&#xff08;或坐标轴&#xff09; 3、去掉图片周边白边 4、自定义颜色图谱 5、设置坐标轴刻度朝向 import matplotlib.pyplot as plt 1、取消坐标轴刻度 ax plt.subplot() ax.set_xticks([]) ax.set_yticks([]) 2、自定…

自定义 视频/音频 进度条

复制代码根据自己需求改动就可以了 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta http-equiv"X-UA-Compatible" content"IEedge" /><metaname"viewport"conten…

模仿火星科技 基于cesium+水平面积测量+可编辑

​ 当您进入Cesium的编辑水平积测量世界&#xff0c;下面是一个详细的操作过程&#xff0c;帮助您顺利使用这些功能&#xff1a; 1. 创建提示窗&#xff1a; 启动Cesium应用&#xff0c;地图场景将打开&#xff0c;欢迎您进入编辑模式。 在屏幕的一角&#xff0c;一个友好的提…

计算机网络:网络通信相关概念入门

目录 一、网络发展背景二、理解网络通信三、理解IP地址1.简述IP地址2.IP地址的版本3.提高地址利用率的技术 四、理解端口1.简述端口2.使用端口的原因 五、理解网络通信协议 一、网络发展背景 网络发展背景&#xff1a; 最初的计算机是单机&#xff0c;那么单机是这样传输数据的…