Dev Collection
HomeGitHubAI Web
  • ReadMe
  • How to use patch-package
  • Add GCC for node-canvas in Centos7.x
  • Create Server-Sent Events in Nextjs
  • TimeZone in Jest
  • Common Git Commit Message Types
  • Node v8 memory leak
  • minIO + picGO self-hosted image hosting service
  • Preventing SSH remote brute-force attacks
  • Ubuntu安装Netflix-Proxy
  • SNIProxy+Dnsmasq实现Netflix-Proxy
  • Simultaneously Pushing a Repository to Two Different Remotes: GitHub and GitLab
  • Typescript中的类型谓词
  • Mac Sonoma 无法安装 Charles 的解决办法
  • Git 多账号管理:在不同文件夹区分Git账号
  • localStorage VS IndexedDB
  • Chrome Extension 开发指南
Powered by GitBook
On this page
  • 基本概念
  • TL;DR:
  • 特性分析
  • Dexie: 简便地使用 IndexedDB

localStorage VS IndexedDB

PreviousGit 多账号管理:在不同文件夹区分Git账号NextChrome Extension 开发指南

Last updated 1 year ago

基本概念

在Web开发中,本地存储技术是实现离线应用和提高用户体验的关键。localStorage和IndexedDB是两种常用的浏览器本地存储技术,它们各自有不同的特点和适用场景。

  • localStorage:是一种简单的键值存储系统,允许你以字符串的形式存储数据。它的主要用途是缓存数据,以便在用户再次访问网页时能够快速加载。localStorage适用于小型数据存储,如用户偏好设置、会话信息等。

  • IndexedDB:是一个更为复杂的NoSQL数据库,它提供了一个非关系型数据库,可以在客户端存储大量结构化数据。IndexedDB支持多种数据类型,并且可以通过索引快速查询数据。它适用于需要存储大量数据和复杂数据结构的应用场景,如游戏、地图应用等。

TL;DR:

  • localStorage 是一个简单的键值存储,适合小型数据存储,如用户设置。并且有最大容量限制 (5M)。

  • IndexedDB 是一个更复杂的NoSQL数据库,适合存储大量结构化数据,支持异步操作,不会阻塞主线程。

  • Dexie.js 是一个封装了 IndexedDB 的库,简化了 API,使得操作更加容易和高效。使用 Dexie,可以轻松地进行数据的增删改查操作,并且它支持 Promise 和 async/await。

  • 对于需要高效管理和操作大量数据的Web应用,尤其是在离线环境下,使用 IndexedDB 和 Dexie.js 是一个优秀的选择。

特性分析

兼容性

localStorage 兼容性

IndexedDB 兼容性

我们可以看到,在现代浏览器中,主流内核基本已经支持 IndexedDB。

存储容量

  • localStorage的存储容量受限于浏览器和设备的限制,通常在5MB左右。

  • IndexedDB的存储容量则大得多,理论上可以存储数GB的数据,具体取决于用户的硬盘空间。

数据类型支持

  • localStorage仅支持字符串类型的数据。

  • IndexedDB支持多种数据类型,包括字符串、数字、布尔值、日期等,还可以存储二进制数据和对象。

API易用性

  • localStorage 的 API 简单明了,易于上手。一般只需要使用 getItem、setItem、removeItem 等方法即可。而 IndexedDB 的 API 更加复杂,但也更为强大,它支持数据的增删改查,事务处理,索引创建和查找等操作。

  • IndexedDB的API较为复杂,但也更为强大。支持数据的增删改查,事务处理,索引创建和查找,对象仓库的操作等,学习曲线较陡。

IndexedDB的异步优势

IndexedDB的操作是基于异步的,这意味着它不会阻塞主线程,从而不会影响用户界面的响应性。在执行数据库读写操作时,IndexedDB会在后台创建一个新的事务线程,这样可以避免长时间的I/O操作阻塞UI线程,确保Web应用的流畅运行。

这种异步性对于Web应用来说非常重要,尤其是对于需要处理大量数据或者复杂计算的应用。例如,一个需要在浏览器端处理大量数据的复杂 Web 应用,如图片编辑器或者大型游戏等,可能需要加载和处理大量的资源和用户数据,这些操作同步进行,可能会导致应用卡顿甚至崩溃,此时就应该选择 IndexedDB 作为你的数据存储方案。同样的,如果正在开发一个 PWA(Progressive Web App) ,它需要在离线状态下也可以访问大量数据,IndexedDB 也会是一个好的选择。因为它既能支持大量的数据存储,又能通过异步操作保证应用的流畅运行。

由于 IndexedDB 操作是异步的,并且默认情况下不会阻塞主线程,因此它通常不会直接影响 TBT。

IndexedDB 核心概念

IndexedDB 的核心组成部分包括数据库(Database)、对象仓库(Object Store)、索引(Index)和事务(Transaction)。

对象仓库是 IndexedDB 中存储数据的地方,你可以把它想象成关系数据库中的表。

索引是与对象仓库相关联的,它可以让你高效地查询数据。

事务用来处理数据库的读写操作,以保证数据库的完整性和一致性。

以下是一个简单的 CRUD 的操作示例

// 打开或创建一个 IndexedDB 数据库
let request = window.indexedDB.open("myDatabase", 1);

// 添加数据记录
request.onsuccess = function(event) {
  let db = event.target.result;
  let transaction = db.transaction(["myObjectStore"], "readwrite");
  let objectStore = transaction.objectStore("myObjectStore");
  objectStore.add({ id: 1, name: "my name" });
};

// 读取数据记录
transaction = db.transaction(["myObjectStore"]);
objectStore = transaction.objectStore("myObjectStore");
let request = objectStore.get(1);
request.onsuccess = function(event) {
  console.log("Name: ", request.result.name);
};

// 更新数据记录
transaction = db.transaction(["myObjectStore"], "readwrite");
objectStore = transaction.objectStore("myObjectStore");
request = objectStore.get(1);
request.onsuccess = function(event) {
  request.result.name = "new name";
  objectStore.put(request.result);
};

// 删除数据记录
transaction = db.transaction(["myObjectStore"], "readwrite");
objectStore = transaction.objectStore("myObjectStore");
objectStore.delete(1);

// 使用索引查询数据
transaction = db.transaction(["myObjectStore"]);
objectStore = transaction.objectStore("myObjectStore");
index = objectStore.index("name");
request = index.get("new name");
request.onsuccess = function(event) {
  console.log("ID: ", request.result.id);
};

Dexie: 简便地使用 IndexedDB

import Dexie from 'dexie';

const db = new Dexie('MyDatabase');
db.version(1).stores({
    friends: 'name, age' // 定义对象仓库和索引
});

// 添加数据
db.friends.add({
    name: 'John',
    age: 25
});

// 使用索引查询
db.friends.where('age').below(30).each(friend => {
    console.log(friend.name);
});

可以看到使用 Dexie.js 让代码更加简洁清晰,更易于理解和维护。

Dexie.js 的核心特性

  • 易于使用的 API:Dexie.js 提供了一个更易于使用的 API,隐藏了 IndexedDB 的复杂性,它提供了一个类似于 SQL 数据库的 API,包括表的创建、数据的增删改查等操作。

  • Promise 和 async/await 支持:Dexie.js 的所有操作都是基于 Promise 的,并且可以与 async/await 配合使用,使得异步代码更加简洁。

  • 事务管理:Dexie.js 提供了对 IndexedDB 事务的封装,使得你可以轻松地管理数据的一致性和完整性。

  • 索引和查询:Dexie.js 支持创建多种类型的索引,包括唯一索引、多值索引和复合索引,以及执行复杂的查询操作。

  • 错误处理:Dexie.js 提供了更加清晰的错误处理机制,使得调试和错误追踪更加容易。

  • 性能优化:Dexie.js 利用了 IndexedDB 的一些高级特性来优化性能,例如批量操作和避免不必要的事件监听。

使用示例

假设我们现在要在一个 web 应用中使用 Dexie 和 IndexedDB。我们可用新建一个 db.ts

shared/db.ts
import Dexie, { Table } from 'dexie'

interface DBChatItem {
    id?: number
    chatId: string
    role: string
    text: string
    timestamp: number
}

class IndexedDexie extends Dexie {
    chats!: Table<DBChatItem>
    
    constructor(dbname: string) {
        super(dbname)
        this.version(1.0).stores({
            chats: '++id, chatId, role, text, timestamp'
        })
    }
}

export const chatDb = new IndexedDexie('chatDb')

现在我们有了一个版本号为 1.0,名称为 chatDb 的 IndexedDB

当我们需要使用这个 chatDb 的时候,我们只需要在其他文件中:

import { chatDb } from '../shared/db'

// 新增对话记录
function addNewChat (text) {
    chatDb.chats.add({
        chatId: 1,
        role: "user",
        text: text,
        timestamp: Date.now(),
    })
}

// 更新某一条对话记录
async function updateChat (chatId, text) {
    const theChatInDB = await chatDb.chat.get({ chatId })
    if (theChatInDB?.id) {
        await chatDb.chat.update(theChatInDB.id, { text })
    }
}

无论是直接使用 IndexedDB 还是通过 Dexie 这样的库来操作,都能够显著提升 Web 应用的性能和用户体验。它们使得 Web 应用能够离线工作,处理大量数据,并提供快速的响应能力。对于那些寻求更高效、更易于管理的数据库操作的开发需求,使用 Dexie 来操作 IndexedDB 是一个合适的方案。

即使IndexedDB 相比 localStorage 有诸多优点,但正是由于其复杂的 API,导致在一般项目中很难看到其身影。这里推荐使用。Dexie.js 是一个处理 IndexedDB 的 JavaScript 库。

Dexie