IndexDB适用于什么场景?如何使用IndexDB?

IndexedDB 是一个浏览器内置的、低级的 API,用于在客户端(用户的浏览器中)存储大量结构化数据。它是一个功能强大的事务型、面向对象的数据库系统。

可以理解为他是一个在你的浏览器里面内置的一个客户端浏览器,类似SQLite这种集成式客户端数据库。

IndexDB核心特点

  • 存储大量数据:相比 localStorage(通常限制在 5MB 左右)和 sessionStorage,IndexedDB 可以存储显著更多的数据(具体上限由浏览器和用户磁盘空间决定,通常是磁盘空间的很大一部分)。
  • 结构化数据: 数据存储在"对象仓库"(类似数据库的表)中,每个仓库可以包含任意数量的 JavaScript 对象(键值对)。这些对象的结构可以很复杂(嵌套对象、数组等)。
  • 键值存储: 每个对象仓库中的对象都必须有一个唯一的"键"(key)来标识它。你可以使用各种类型作为键(数字、字符串、日期、数组等)。
  • 索引查询: 这是 IndexedDB 名字的由来和强大之处!你可以在对象仓库上创建索引。索引允许你根据对象中除主键之外的属性值进行高效的查询和排序。没有索引的话,查询大量数据需要遍历整个仓库,效率极低。
  • 事务性: 所有的数据库操作(读取、写入、更新、删除)都必须在事务中执行。事务确保操作的原子性:要么所有操作都成功完成,要么如果中途出错,所有操作都会被回滚,数据库保持操作前的状态。这保证了数据的一致性。
  • 异步 API: IndexedDB 的所有操作都是异步的,使用请求和回调(或更现代的 Promise 封装库)来处理结果。这避免了阻塞主线程,保持页面响应。
  • 同源策略: 每个源的 IndexedDB 数据库是独立的,只能由相同源(协议、域名、端口)的页面访问。
  • 支持二进制数据: 可以存储 ArrayBuffer、Blob(如图片、文件)、File 对象等二进制数据。

适用场景

IndexedDB 非常适合需要在客户端存储和管理大量结构化数据,并且需要高效查询能力的 Web 应用程序。

  1. 离线优先应用 / PWA

    • 核心场景!允许应用在没有网络连接时完全正常运行。
    • 将应用所需的数据(用户数据、产品目录、文章、配置等)完整存储在本地。
    • 用户离线时进行读写操作(如填写表单、编辑文档)。
    • 网络恢复后,再将本地修改的数据同步回服务器。
  2. 处理大量数据的应用

    • 数据可视化/分析仪表盘: 在本地存储大量数据集,支持快速的过滤、排序、聚合操作,无需频繁请求服务器。
    • 富文本编辑器/笔记应用: 存储文档历史版本、大型文档内容、附件等。
    • 邮件客户端: 在本地存储大量邮件、附件,实现快速的搜索、分类、离线阅读。
    • 音乐/媒体库管理: 存储媒体文件元数据、播放列表、用户偏好等。
  3. 缓存大型或频繁访问的数据

    • 缓存从服务器获取的 API 响应结果(尤其是大型数据集),减少网络请求,提升后续访问速度。
    • 缓存用户生成的内容草稿,防止意外丢失。

其实还有一种应用也可以使用IndexDB:聊天软件!聊天记录通常都保存在本地,更方便快速查看,也可以离线查看。大部分的聊天软件都是APP,所以用的都是内置的sql来做,比如SQLite。

使用IndexDB

核心API对象概览

  1. indexedDB - 全局入口对象
ini 复制代码
// 打开/创建数据库
const request = window.indexedDB.open('myDatabase', 1);
  1. IDBFactory (indexedDB 的实现接口) * .open(name, version) → 打开数据库 * .deleteDatabase(name) → 删除数据库 * .cmp(a, b) → 比较两个键的大小(返回 -1/0/1)

  2. IDBOpenDBRequest (数据库打开请求对象)

    • onsuccess - 数据库打开成功事件回调
    • onupgradeneeded - 需要版本升级时触发事件回调
    • onerror - 打开失败事件回调
  3. IDBDatabase (数据库实例)

    • .createObjectStore(name, options) → 创建对象仓库
    • .deleteObjectStore(name) → 删除对象仓库
    • .transaction(storeNames, mode) → 创建事务
    • .close() → 关闭数据库连接
ini 复制代码
request.onsuccess = (event) => {
  const db = event.target.result;
};
  1. IDBObjectStore (对象仓库,类似数据库表) * .add(value, key?) → 添加数据(键需唯一) * .put(value, key?) → 更新或插入数据 * .get(key) → 通过主键获取数据 * .delete(key) → 删除数据 * .clear() → 清空仓库 * .index(name) → 获取索引对象 * .createIndex(name, keyPath, options) → 创建索引 * .deleteIndex(name) → 删除索引 * .openCursor(range?, direction?) → 打开游标 * .count(query?) → 统计记录数
ini 复制代码
const store = db.createObjectStore('books', { keyPath: 'id' });
  1. IDBIndex (索引对象) * .get(key) → 通过索引键获取单条数据 * .getKey(key) → 获取主键 * .getAll(query?, count?) → 获取多条数据 * .openCursor(range?, direction?) → 打开游标(基于索引) * .openKeyCursor(range?, direction?) → 打开键游标
ini 复制代码
const index = store.createIndex('author_idx', 'author');
  1. DBTransaction (事务控制器) * .objectStore(name) → 获取对象仓库 * .mode → 事务模式 (readonly/readwrite/versionchange) * .commit() → 手动提交事务 * .abort() → 中止事务 * oncomplete → 事务成功完成 * onerror → 事务失败
ini 复制代码
const tx = db.transaction('books', 'readwrite');
  1. IDBRequest (异步操作请求对象) * onsuccess → 操作成功 * onerror → 操作失败 * .result → 操作结果 * .error → 错误信息

  2. IDBCursor (数据游标)

    • .continue(key?) → 移动到下一条
    • .advance(count) → 跳过指定条数
    • .update(value) → 更新当前记录
    • .delete() → 删除当前记录
    • .key → 当前键
    • .value → 当前值
    • .direction → 游标方向 (next/prev/nextunique/prevunique)
ini 复制代码
store.openCursor().onsuccess = (event) => {
  const cursor = event.target.result;
  if (cursor) {
    console.log(cursor.value);
    cursor.continue(); // 移动到下一条
  }
};
  1. IDBKeyRange (键范围工具)
    • .only(key) → 等于指定键
    • .lowerBound(min, open?) → 最小值范围
    • .upperBound(max, open?) → 最大值范围
    • .bound(min, max, lowerOpen?, upperOpen?) → 双边界范围
go 复制代码
// 创建范围:18 ≤ age < 30
const range = IDBKeyRange.bound(18, 30, false, true);

工作流程

  1. 打开数据库
ini 复制代码
const request = indexedDB.open('library', 2);

request.onupgradeneeded = (event) => {
  const db = event.target.result;
  // 创建对象仓库
  if (!db.objectStoreNames.contains('books')) {
    const store = db.createObjectStore('books', { keyPath: 'isbn' });
    store.createIndex('by_author', 'author', { unique: false });
  }
};

注意 :打开数据库后创建对象仓库(数据库表)要在onupgradeneeded回调里进行,不能再onsuccess回调里执行。这是IndexDB的关键设计限制。数据库结构修改(创建/删除对象仓库)只能在 onupgradeneeded 事件中进行。当 onsuccess 触发时,数据库已经处于"就绪"状态,不允许修改结构。

  1. 写入数据
ini 复制代码
const tx = db.transaction('books', 'readwrite');
const store = tx.objectStore('books');

store.add({
  isbn: '978-3-16-148410-0',
  title: 'JavaScript: The Good Parts',
  author: 'Douglas Crockford'
});

tx.oncomplete = () => console.log('Data saved');
  1. 查询数据
javascript 复制代码
// 通过主键查询
store.get('978-3-16-148410-0').onsuccess = (e) => {
  console.log('Book:', e.target.result);
};

// 通过索引查询
const index = store.index('by_author');
index.getAll('Douglas Crockford').onsuccess = (e) => {
  console.log('All books:', e.target.result);
};
  1. 范围查询
ini 复制代码
// 查询价格在 $20-$50 之间的书籍
const priceRange = IDBKeyRange.bound(20, 50);
const priceIndex = store.index('by_price');

priceIndex.openCursor(priceRange).onsuccess = (e) => {
  const cursor = e.target.result;
  if (cursor) {
    console.log(cursor.value);
    cursor.continue();
  }
};
  1. 删除数据
ini 复制代码
store.delete('978-3-16-148410-0').onsuccess = () => {
  console.log('Book deleted');
};

注意事项

  1. 事务自动提交机制

事务会在所有请求完成且没有新请求时自动提交,显式调用 transaction.commit() 可提前提交

  1. 版本管理

数据库版本号必须是整数,修改结构需在 onupgradeneeded 中完成

  1. 游标遍历

游标必须通过 continue() 或 advance() 主动移动,否则只会访问第一条记录

  1. 错误处理层级

错误处理需在多个层级设置:

ini 复制代码
// 数据库级
db.onerror = handleError;

// 事务级
tx.onerror = handleError;

// 请求级
request.onerror = handleError;
  1. 键路径类型 * 数字/字符串/Date 对象 * 数组(复合键) * 二进制数据(ArrayBuffer)

一个小Demo

javascript 复制代码
'use client'

import { useEffect, useState } from "react";

export default function IndexDB() {
  const [db, setDB] = useState<IDBDatabase | null>(null)

  useEffect(() => {
    if (!db) {
      // 打开/创建数据库
      const request = window.indexedDB.open('library', 2);

      // 在 onupgradeneeded 中创建数据库的表结构
      request.onupgradeneeded = (event) => {
        const db = event.target?.result;
        // 创建对象仓库 books
        if (!db.objectStoreNames.contains('books')) {
          const store = db.createObjectStore('books', { keyPath: 'id', autoIncrement: true });
          store.createIndex('by_author', 'author', { unique: false });
        }
        // 创建对象仓库 authors
        if (!db.objectStoreNames.contains('authors')) {
          db.createObjectStore('authors', { keyPath: 'id', autoIncrement: true });
        }
        // 既可以继续创建你应用的其他对象仓库...
      };

      // 监听数据库打开成功事件
      request.onsuccess = (event) => {
        const db = event.target?.result;
        console.log('数据库打开成功', db);
        setDB(db)
      };
      // 监听数据库打开失败事件
      request.onerror = (event) => {
        console.error('数据库打开失败', event);
      };
    }
  }, [db])

  const addHandle = () => {
    // 新增数据
    const tx = db?.transaction('books', 'readwrite')
    const store = tx?.objectStore('books')
    store?.add({title: '三毛流浪记', date: '2025-08-7'})

    // 链式写法
    db?.transaction('books', 'readwrite').objectStore('books').add({
      title: '鲁滨逊漂流记',
      date: '2025-08-07',
      author: 'Lonely'
    })
  }

  const searchList = () => {
    const tx = db?.transaction('books', 'readwrite')
    const store = tx?.objectStore('books')

    // 查询全部
    store?.getAll().onsuccess = ((res) => {
      console.log('all Book', res.target.result);
    })

    // 通过自增ID查询
    // store?.get(8).onsuccess = ((res) => {
    //   console.log('res', res?.target?.result);
    // })
  }

  return <div>
    IndexDB<br/>
    <button onClick={addHandle}>add</button><br/>
    <button onClick={searchList}>search</button>
  </div>;
}

点击add便能添加两条数据,点击search就可以查看添加的数据

相关推荐
cpp加油站2 分钟前
打脸来的太快了,又发现一个Trae的宝藏功能--内置浏览器可以指定机型来显示前端界面
前端·ai编程·trae
Web极客码15 分钟前
如何为WordPress启用LiteSpeed缓存
前端·缓存
咕噜分发企业签名APP加固彭于晏31 分钟前
白嫖价值千元的EO
前端·javascript
前端开发爱好者33 分钟前
首个「完整级」WebSocket 调试神器来了!
前端·javascript·vue.js
前端Hardy35 分钟前
HTML&CSS&JS:高颜值登录注册页面—建议收藏
前端·javascript·css
白雾茫茫丶37 分钟前
如何动态执行 JS 脚本
javascript
Ali酱38 分钟前
远程这两年,我才真正感受到——工作,原来可以不必吞噬生活。
前端·面试·远程工作
金金金__43 分钟前
优化前端性能必读:浏览器渲染流程原理全揭秘
前端·浏览器
Data_Adventure1 小时前
Vue 3 手机外观组件库
前端·github copilot
泯泷1 小时前
Tiptap 深度教程(二):构建你的第一个编辑器
前端·架构·typescript