☰
IndexedDB 本地数据库
2026/9/25 18:35:18 网站建设 项目流程

5.4 IndexedDB 本地数据库

IndexedDB 是浏览器原生提供的事务型结构化本地数据库,专为解决 Web Storage 容量上限低、仅支持字符串存储、查询能力弱的问题。

它支持大容量存储、索引查询、事务处理,能够存储海量结构化业务数据与二进制资源,是复杂离线应用、大数据本地缓存的核心存储方案。


5.4.1 核心概念与基础操作

一、核心特性
  • 大容量存储:单域名存储上限通常在 50MB ~ 数百MB 级别,远高于 Web Storage 的 5MB,支持存储文件、二进制大对象。
  • 结构化存储:以键值对形式存储 JavaScript 对象、数组、二进制数据等,无需手动序列化,支持嵌套数据结构。
  • 异步非阻塞:所有操作均为异步事件驱动,基于回调执行,不会阻塞主线程,避免大数据量操作导致页面卡顿。
  • 事务型保证:所有数据操作都在事务中执行,具备原子性、一致性,操作失败自动回滚,保证数据完整性。
  • 索引与查询:支持创建多字段索引,通过索引实现高效条件查询;支持游标遍历与范围筛选。
  • 同源隔离:严格遵循同源策略,不同站点无法访问对方的数据库,保障数据安全。
二、核心概念

IndexedDB 概念

类比关系型数据库

含义说明

Database(数据库)

数据库实例

独立的存储单元,通过名称唯一标识,支持多版本管理

Object Store(对象仓库)

数据表

数据存储的集合,存储键值对记录,每个仓库对应一类业务数据

Index(索引)

索引

基于对象仓库的字段创建,用于加速条件查询,支持唯一约束

Transaction(事务)

事务

数据操作的执行单元,所有读写都必须在事务中进行,分为只读/读写两种模式

Cursor(游标)

遍历指针

用于逐条遍历对象仓库或索引中的数据,支持范围筛选与增量读取

三、基础操作语法
1. 打开/创建数据库

通过indexedDB.open(dbName, version)打开数据库,若数据库不存在则自动创建;版本号提升时触发升级事件。 数据库结构的修改(创建仓库、创建索引)只能在upgradeneeded事件中执行,这是 IndexedDB 版本管理的核心机制。

// 打开名为 UserDB 的数据库,版本号为 1 const request = indexedDB.open('UserDB', 1); let db = null; // 数据库升级时触发:首次创建、版本号提升 request.onupgradeneeded = function(e) { db = e.target.result; // 创建对象仓库(相当于表),指定主键为 id const userStore = db.createObjectStore('users', { keyPath: 'id' }); // 创建索引:按用户名查询,唯一索引 userStore.createIndex('name', 'name', { unique: true }); // 创建索引:按年龄查询,非唯一索引 userStore.createIndex('age', 'age', { unique: false }); }; // 数据库打开成功 request.onsuccess = function(e) { db = e.target.result; console.log('数据库初始化完成'); }; // 数据库打开失败 request.onerror = function(e) { console.error('数据库打开失败:', e.target.error.message); };
2. 新增数据

使用readwrite模式的事务,add()方法用于纯新增,主键重复会抛出错误;put()方法为「新增或覆盖更新」,主键存在则更新、不存在则新增。

function addUser(user) { // 创建读写事务,指定操作的仓库 const transaction = db.transaction(['users'], 'readwrite'); // 获取对象仓库实例 const store = transaction.objectStore('users'); // 执行新增操作 const request = store.add(user); request.onsuccess = () => console.log('用户新增成功'); request.onerror = () => console.error('用户新增失败'); } // 调用示例 addUser({ id: 1, name: 'zhangsan', age: 25, city: 'beijing' });
3. 查询数据
  • 主键查询:get(key) 按主键获取单条数据
  • 全量查询:getAll() 获取仓库中所有数据
  • 索引查询:通过索引对象按指定字段快速查询
// 1. 按主键查询单条数据 function getUserById(id) { const transaction = db.transaction(['users'], 'readonly'); const store = transaction.objectStore('users'); const request = store.get(id); request.onsuccess = () => { console.log('查询结果:', request.result); }; } // 2. 按索引查询 function getUserByName(name) { const transaction = db.transaction(['users'], 'readonly'); const store = transaction.objectStore('users'); const nameIndex = store.index('name'); // 获取索引对象 const request = nameIndex.get(name); request.onsuccess = () => { console.log('按名称查询结果:', request.result); }; }
4. 更新与删除
// 更新数据:put 方法,主键存在则覆盖更新 function updateUser(user) { const transaction = db.transaction(['users'], 'readwrite'); const store = transaction.objectStore('users'); store.put(user); } // 按主键删除单条数据 function deleteUser(id) { const transaction = db.transaction(['users'], 'readwrite'); const store = transaction.objectStore('users'); store.delete(id); } // 清空仓库所有数据 function clearAllUsers() { const transaction = db.transaction(['users'], 'readwrite'); const store = transaction.objectStore('users'); store.clear(); }
5. 游标遍历

适合大批量数据遍历、范围查询场景,避免一次性加载全部数据到内存,支持增量处理。

function traverseAllUsers() { const transaction = db.transaction(['users'], 'readonly'); const store = transaction.objectStore('users'); const request = store.openCursor(); // 打开游标 request.onsuccess = function(e) { const cursor = e.target.result; if (cursor) { console.log('当前记录:', cursor.value); cursor.continue(); // 移动到下一条 } else { console.log('遍历完成'); } }; }

5.4.2 常用封装思路

原生 IndexedDB API 基于事件回调,写法繁琐、容易形成回调地狱,且版本管理、错误处理分散。实际项目中通常会进行分层封装,简化调用方式,统一管理底层逻辑。

一、封装核心目标
  1. 回调 Promise 化:将事件回调转为 Promise,支持 async/await 语法,大幅提升代码可读性与可维护性。
  2. 接口简化:屏蔽事务、请求对象等底层概念,对外提供简洁的增删改查业务方法。
  3. 统一管理:集中处理数据库打开、版本升级、错误捕获、事务生命周期管理。
二、基础 Promise 封装

最轻量化的封装,将单次操作包装为 Promise,适合简单业务场景快速接入。

// 封装打开数据库 function openDB(dbName, version, onUpgrade) { return new Promise((resolve, reject) => { const request = indexedDB.open(dbName, version); request.onupgradeneeded = e => onUpgrade(e.target.result); request.onsuccess = e => resolve(e.target.result); request.onerror = e => reject(e.target.error); }); } // 封装新增操作 function addItem(db, storeName, data) { return new Promise((resolve, reject) => { const transaction = db.transaction([storeName], 'readwrite'); const store = transaction.objectStore(storeName); const request = store.add(data); request.onsuccess = () => resolve(request.result); request.onerror = () => reject(request.error); }); } // 使用示例 async function demo() { const db = await openDB('DemoDB', 1, (db) => { // 升级回调中创建仓库 db.createObjectStore('items', { keyPath: 'id' }); }); await addItem(db, 'items', { id: 1, title: '测试数据' }); }
三、类式封装

将数据库操作封装为独立工具类,统一管理数据库实例、配置与生命周期,是中型项目的主流方案。

class IndexedDBHelper { constructor(dbName, version, storesConfig) { this.dbName = dbName; this.version = version; this.storesConfig = storesConfig; // 仓库配置:名称、主键、索引 this.db = null; } // 初始化数据库 init() { return new Promise((resolve, reject) => { const request = indexedDB.open(this.dbName, this.version); request.onupgradeneeded = (e) => { const db = e.target.result; // 批量创建对象仓库与索引 this.storesConfig.forEach(config => { if (!db.objectStoreNames.contains(config.name)) { const store = db.createObjectStore(config.name, { keyPath: config.keyPath }); config.indexes?.forEach(idx => { store.createIndex(idx.name, idx.field, { unique: idx.unique || false }); }); } }); }; request.onsuccess = (e) => { this.db = e.target.result; resolve(this); }; request.onerror = (e) => reject(e.target.error); }); } // 新增数据 add(storeName, data) { return this._execute(storeName, 'readwrite', store => store.add(data)); } // 按主键查询 get(storeName, key) { return this._execute(storeName, 'readonly', store => store.get(key)); } // 通用事务执行方法,统一封装 Promise _execute(storeName, mode, handler) { return new Promise((resolve, reject) => { const transaction = this.db.transaction([storeName], mode); const store = transaction.objectStore(storeName); const request = handler(store); request.onsuccess = () => resolve(request.result); request.onerror = () => reject(request.error); }); } } // 使用示例 const db = new IndexedDBHelper('AppDB', 1, [ { name: 'users', keyPath: 'id', indexes: [{ name: 'name', field: 'name', unique: true }] } ]); db.init().then(async () => { await db.add('users', { id: 1, name: 'lisi', age: 30 }); const user = await db.get('users', 1); });
四、开源库方案

复杂项目推荐使用成熟的开源封装库,避免重复造轮子:

  • Dexie.js:最主流的 IndexedDB 封装库,提供类似 MongoDB 的链式查询 API,完整支持事务、索引、范围查询,功能完善,生态成熟。
  • localForage:API 风格与 Web Storage 高度一致,简单易用,自动降级适配 Web Storage、IndexedDB 等多种存储引擎,适合简单的键值存储场景。
五、适用场景与边界
  • 适用场景:海量结构化业务数据存储、离线应用数据持久化、大文件/二进制数据本地缓存、复杂条件查询场景。
  • 不适用场景:少量简单配置数据优先使用 localStorage;强实时性的高频读写场景需注意异步开销。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询