前端本地储存数据库 IndexedDB 零基础入门知识详解

目录

前言:

1.IndexedDB 简介

2.IndexedDB 使用场景

3.IndexedDB 特点

(1) 非关系型数据库(NoSql)

(2) 持化存储

(3)异步操作

 (4)支持事务

(5) 同源策略

(6)存储容量大

4.IndexedDB 概念补充

①仓库objectStore

②索引index

③游标cursor

④事务

5.IndexedDB 实操

①创建或连接数据库

② 插入数据

③通过主键读取数据 

④通过游标查询数据

⑤通过索引查询数据

⑥通过索引和游标查询数据

⑦通过索引和游标分页查询

⑧更新数据

⑨通过主键删除数据

⑩通过索引和游标删除指定数据

⑪关闭数据库

⑫删除数据库


前言:

在浏览器上有两种数据库: webSQL和IndexedDB。但是如果在浏览器上需要用到数据库一般会使用Indexed DB数据库,webSQL基本上已经废弃了。

WebSQL 是一种基于 SQL 的浏览器本地存储技术,在 HTML5 中是一个非标准化技术。虽然 WebSQL 在一些场景下使用起来比较方便,但是由于其不被 W3C 标准化,导致了一些问题:

  1. 浏览器之间的兼容性问题。不同浏览器对 WebSQL 的支持程度不一样,甚至有些浏览器完全不支持,这导致了 WebSQL 在移动端和 PC 端之间的兼容性问题。
  2. 安全性问题。WebSQL 存在一些潜在的安全风险,例如攻击者可以通过注入恶意 SQL 语句来获取用户数据等。
  3. 数据库规模限制。WebSQL 的数据库容量限制是各个浏览器自己实现的,没有一个通用的标准,而且大部分浏览器都对数据库大小进行了限制。

因此,W3C 推出了 IndexedDB 标准,作为 WebSQL 的替代方案,并得到了主流浏览器的支持。IndexedDB 具有更好的跨平台兼容性,安全可靠,支持事务和异步操作等特性,能够更好地应对现代移动端应用和在线数据存储应用的需求。

(前端存储方案有如cookie、sessionstorage等等)


1.IndexedDB 简介

MDN官网是这样解释 Indexed DB的:

IndexedDB 是一种底层 API,用于在客户端存储大量的结构化数据 (也包括文件/二进制大型对象 (blobs))。该 API 使用索引实现对数据的高性能搜索。虽然 Web Storage 在存储较少量的数据很有用,但对于存储更大量的结构化数据来说力不从心。而 IndexedDB 提供了这种场景的解决方案。

 cookie、localStorage 等存储方式都有存储大小限制,如果数据量很大,且都需要客户端存储时,则使用 IndexedDB 数据库。

会话期 Cookie  持久性 Cookie sessionSto
rage
localStora
ge
indexedDB WebsQL
存储大小 4kb 4kb 2.5~10MB 2.5~10MB >250MB 已废弃
失效时间 浏览器关闭自动清除 设置过期时间,到
期后清除
浏览器关
闭后清除
永久保存
(除非手
动清除)
手动更新
或删除
已废弃
与服务端交互 已废弃
访问策略 符合同源策略可以访问 符合同源策略可以
访问
符合同源
策略可以
访问
即使同源
也不可相
互访问
符合同源
策略可以
已废弃
访问器
已废弃


2.IndexedDB 使用场景

所有的场景都基于客户端需要存储大量数据的前提下:

  1. 数据可视化等界面,大量数据,每次请求会消耗很大性能。
  2. 即时聊天工具,大量消息需要存在本地。
  3. 其它存储方式容量不满足时,不得已使用 IndexedDB


3.IndexedDB 特点

(1) 非关系型数据库(NoSql)

MySQL等数据库都是关系型数据库,它们的主要特点就是数据都以一张二维表的形式存储,而Indexed DB是非关系型数据库,主要以键值对的形式存储数据。


(2) 持化存储

cookie、localStorage、 sessionStorage 等方式存储的数据当我们清楚浏览器缓存后,这些数据都会被清除掉的,而使用IndexedDB存储的数据则不会,除非手动删除该数据库。


(3)异步操作

 IndexedDB 操作时不会锁死浏览器(异步),用户依然可以进行其他的操作,这与localstorage形成鲜明的
对比,后者是同步的。

 (4)支持事务

IndexedDB 支持事务 (transaction),这意味着一系列的操作步骤之中,只要有一步失败了,整个事务都会取消,数据库回滚的事务发生之前的状态,这和MySQL等数据库的事务类似。

(5) 同源策略

IndexedDB同样存在同源限制,每个数据库对应创建它的域名。网页只能访问自身域名下的数据库,而不能访问跨域的数据库。

(6)存储容量大

IndexedDB最显著的特点之一了,也是不用localStorage等存储方式的最好理由。


4.IndexedDB 概念补充

①仓库objectStore

IndexedDB 它只有仓库 store 的概念,可以把仓库理解为表即可,即一个 store 没有表的概念,是一张表。

②索引index

在关系型数据库当中也有索引的概念,我们可以给对应的表字段添加索引,以便加快查找速率。在IndexedDB 中同样有索引,可以在创建store的时候同时创建索引,在后续对store进行查询的时候即可通过索引来筛选,给某个字段添加索引后,在后续插入数据的过成功,索引字段便不能为空。

③游标cursor

游标是IndexedDB数据库新的概念,可以把游标想象为一个指针,当要查询满足某一条件的所有数据时,就需要用到游标,让游标一行一行的往下走,游标走到的地方便会返回这一行数据,此时便可对此行数据进行判断,是否满足条件。
⭕注意:IndexedDB 查询不像 MySQL等数据库方便它只能通过主键、索引、游标方式查询数据。

④事务

IndexedDB支持事务,即对数据库进行操作时,只要失败了的一致性。都会回滚到最初始的状态,确保数据 


5.IndexedDB 实操

IndexedDB 所有针对仓库的操作都是基于事务的。

①创建或连接数据库

//打开数据库
//@param {object} dbName 数据库的名字
//@param {string} storeName 仓库名称
//@param {string} version 数据库的版本
//@return {object} 该函数会返回一个数据库实例

function openDB(dbName, version = 1) {
    return new Promise((resolve, reject) =>{
        // 兼容浏览器
        var indexedDB =
            window.indexedDB ||
            window.mozIndexedDB ||
            window.webkitIndexedDB ||
            window.msIndexedDB;
        let db;
        // 打开数据库,若没有则会创建
        const request = indexedDB.open(dbName,version);

        //数据库打开成功回调
        request.onsuccess = fuction(evetn){
            db = event.target.result; //数据库对象
            console.log("数据库打开成功");
            resolve(db);
        };
        //数据库打开失败的回调
        request .onerror= function(event){
            console.log("数据库打开报错");
        };

        //数据库有更新时候的回调
        request.onupgradeneeded = function (event){
            // 数据库创建或升级的时候会触发
            console.log("onupgradeneeded");
            db = event.target.result; //数据库对象var objectstore;
            // 创建存储库
            objectstore = db.createobjectstore("signalChat",{
                keyPath:"sequenceId",// 这是主键
                // autoIncrement: true // 实现自增
            });
            // 创建索引,在后面查询数据的时候可以根据索引查        
            objectStore.createIndex("link","link", { unique:false});
            objectstore.createIndex("sequenceId","sequenceId", {unique: false});
            objectStore.createIndex("messageType","messageType",{
                unique: false,
            });
        };
    });
}

将创建数据库的操作封装成了一个函数,并且该函数返回一个 promise 对象,使得在调用的时候可以链式调用,函数主要接收两个参数: 数据库名称、数据库版本。函数内部主要有三个回调函数,分别是:

  • onsuccess: 数据库打开成功或者创建成功后的回调,这里我们将数据库实例返回了出去。
  • onerror: 数据库打开或创建失败后的回调。
  • onupgradeneeded: 当数据库版本有变化的时候会执行该函数,比如我们想创建新的存储库(表),就可以在该函数里面操作,更新数据库版本即可。

② 插入数据

function addData(db, storeName, data) {
    var request = db
    .transaction([storeName],"readwrite") // 事务对象 指定表格名称和操作模式 ("只读"或"读写")
    .objectStore(storeName) // 仓库对象
    .add(data);

    request.onsuccess = function (event){    
        console.log("数据写入成功");
    };

    request.onerror = function (event) {
        console.log("数据写入失败");
    };
}

 IndexedDB 插入数据需要通过事务来进行操作,插入的方法也很简单,利用 IndexedDB 提供的 add方法即可,这里同样将插入数据的操作封装成了一个函数,接收三个参数,分别如下:

  • db: 在创建或连接数据库时,返回的db实例,需要那个时候保存下来
  • storeName: 仓库名称(或者表名),在创建或连接数据库时就已经创建好了仓库。
  • data: 需要插入的数据,通常是一个对象

 ⭕注意 :  插入的数据是一个对象,而且必须包含声明的索引键值对


③通过主键读取数据 

function getDataByKey(db, storeName, key) {
    return new Promise((resolve, reject) => {
        var transaction = db.transaction([storeName]); // 事务
        var objectstore = transaction.objectstore(storeName); // 仓库对象
        var request = objectstore.get(key); // 通过主键获取数据

        request.onerror = function (event) {
            console.log("事务失败");
        };
    
        request.onsuccess = function (event) {
            console.log("主键查询结果:",,request.result);
            resolve(request.result);
        };
    });
}

④通过游标查询数据

function cursorGetData(db, storeName){
    let list = [];
    var store = db
        .transaction(storeName,"readwrite") // 事务
        .objectstore(storeName); // 仓库对象
    var request = store.openCursor(); // 指针对象
    // 游标开启成功,逐行读数据
    request.onsuccess = function (e) {
        var cursor = e.target.result;
        if (cursor) {
            // 必须要检查
            list.push(cursor.value);
            cursor.continue(); // 遍历了存储对象中的所有内容
        } else {
            console.log("游标读取的数据",list);
        }
    };
}

⑤通过索引查询数据

function getDataByIndex(db,storeName, indexName, indexValue){
    var store = db,transaction(storeName, "readwrite").objectstore(storeName);
    var request = store.index(indexName).get(indexValue);
    request.onerror = function () {
        console.log("事务失败");
    };
    request.onsuccess = function (e) {
        var result = e.target.result;
        console.log("索引查询结果:",result);
    };
}

索引名称 即创建仓库的时候创建的索引名称,也就是键值对中的键,最终会查询出所有满足传入函数索引值的数据。

索引名称即创建仓库的时候创建的索引名称,也就是键值对中的键。最终会查询出仅一条传入函数索引值的数据。


⑥通过索引和游标查询数据

单独通过索引或者游标查询出的数据都是部分或者所有数据,如果想要查询出索引中满足某些条件的所有数据,那么单独使用索引或游标是无法实现的。当然,可以查询出所有数据之后在循环数组筛选出合适的数据,但是这不是最好的实现方式,最好的方式当然是将索引和游标结合起来。

function cursorGetDataByIndex(db,storeName ,indexName ,indexValue ){
    let list = [];
    var store = db.transaction(storeName."readwrite").objectstore(storeName); // 仓库对象
    var request = store
        .index(indexName) // 索引对象
        .openCursor(IDBKeyRange.only(indexValue)); // 指针对象
    request.onsuccess = function (e) {var cursor = e.target.result;
        if (cursor) {
            // 必须要检查
            list.push(cursor.value);
            cursor.continue(); // 遍历了存储对象中的所有内容
        } else {
            console.log("游标索引查询结果:",list);
        }
    };
    request.onerror = function (e) {};
}

⑦通过索引和游标分页查询

IndexedDB 分页查询 不像 MySQL 分页查询那么简单,没有提供现成的API,如 limit 等,所以需要自己实现分页。

function cursorGetDataByIndexAndPage(
    db ,  //数据库实例
    storeName,
    indexName,
    indexValue,
    page,
    pageSize
){
    let list = []; //设置空数组用来保存实例
    let counter = ; // 计数器——停止游标
    let advanced = true; // 是否跳过多少条查询
    var store = db.transaction(storeName, "readwrite"),obiectStore(storeName): // 库对象
    var request = store
        .index(indexName) // 索引对象
        .openCursor(IDBKeyRange.only(indexValue)); // 指针对象
    request.onsuccess = function (e) {
        var cursor = e.target.result;
        if (page > 1 && advanced) {
            advanced = false; //是否跳转
            cursor.advance((page - 1) * pagesize); // 跳过多少条
            return;
        }
        if (cursor) {
                // 必须要检查
                list.push(cursor.value);
                counter++;
                if (counter < pageSize) [
                cursor.continue(); // 遍历了存储对象中的所有内容
            } else {
                cursor = null;
                console.log("分页查询结果",,list);
            }
       } else {
            console.log("分页查询结果",list);
       }
    };    
    request.onerror = function (e) {};
}

IndexedDB的一个APl: advance。该函数可以让游标跳过多少条开始查询。假如额分页是每页10条数据,现在需要查询第2页,那么就需要跳过前面10条数据,从11条数据开始查询,直到计数器等于10,那么就关闭游标,结束查询。


⑧更新数据

IndexedDB更新数据较为简单,直接使用 put 方法,值得注意的是如果数据库中没有该条数据,则会默认增加该条数据,否则更新。喜欢更新和新增都是用put方法,这也是可行的。

function updateDB(db, storeName, data) {
    var request = db
        .transaction([storeName],"readwrite") // 事务对象
        .objectstore(storeName) // 仓库对象
        .put(data);

    request.onsuccess = function () {
        console.log("数据更新成功");
    };

    request.onerror = function () {
        console.log("数据更新失败");
    };
}

 put方法接收一个数据对象


⑨通过主键删除数据

主键即创建数据库时申明的 keyPath,它是唯一的。

function deleteDB(db, storeName, id) {
    var request = db
        .transaction([storeName],"readwrite")
        .objectstore(storeName)
        .delete(id);
    request.onsuccess = function () {
        console.log("数据删除成功");
    };

    request.onerror = function () {
        console.log("数据删除失败");
    };
}

⑩通过索引和游标删除指定数据

有时候拿不到主键值,只能只能通过索引值来删除,通过这种方式,可以删除一条数据(索引值唯一)或者所有满足条件的数据 (索引值不唯一)。

function cursorDelete(db,storeName,indexName,indexValue){
    var store = db,transaction(storeName,"readwrite").objectstore(storeName);
    var request = store
        .index(indexName) // 索引对象
        .openCursor(IDBKeyRange.only(indexValue)); // 指针对象
    request.onsuccess = function (e) {
        var cursor = e.target.result;
        var deleteRequest;
        if (cursor) {
            deleteRequest = cursor.delete(); // 请求删除当前项
            deleteRequest.onerror = function () {
                console.log("游标删除该记录失败");
            };
            deleteRequest.onsuccess = function () {    
                console.log("游标删除该记录成功");
            };
            cursor.continue();
        }
    };
    request.onerror = function (e){ };
}

上段代码可以删除索引值为indexValue的所有数据,值得注意的是使用 IDBKeyRange.only0API,该API代表只能当两个值相等时,具体API解释可参考MDN官网。


⑪关闭数据库

 当数据库操作完毕后,建议关闭它,节约资源。

function closeDB(db) {
    db.close();
    console.log("数据库已关闭");
}

⑫删除数据库

function deleteDBAll(dbName){
    console.log(dbName);
    let deleteRequest = window,indexedDB.deleteDatabase(dbName);
    deleteRequest.onerror = function (event) {
        console.log("删除失败");
    };
    deleteRequest.onsuccess = function (event) {
        console.log("删除成功");
    };
}

总结:

IndexedDB 是 HTML5 标准中定义的一种在浏览器端存储大量结构化数据的 API,它具备以下特点:

  1. 异步 API:IndexedDB 采用异步 API 进行访问和操作,能够更好地应对大量数据存储、高并发数据访问等需求。
  2. 大规模数据存储:IndexedDB 支持存储大量的结构化数据,并支持索引和事务处理等高级操作,适用于需要高性能本地存储大规模数据的场景。
  3. 数据库查询:IndexedDB 支持复杂的查询,包括范围查询和游标查询等,可帮助开发者进行精确查询和高效数据检索。
  4. 数据库设计:IndexedDB 本身是基于 NoSQL 的键值存储数据库,和其他数据库(如 MongoDB)的设计理念相似,能够帮助开发者进行灵活的数据库设计和数据模型设计。

相比于其他的浏览器端存储技术,IndexedDB 具有更好的跨平台兼容性、安全性和扩展性,可以满足现代 Web 应用对于数据存储的各种要求。但是,由于其使用较为复杂,需要开发者深入了解浏览器端存储技术、JavaScript 异步编程、数据库设计和查询等方面才能够充分发挥其优势。

猜你喜欢

转载自blog.csdn.net/m0_61662775/article/details/131029016
今日推荐