Skip to content

IndexedDB 数据库详解 💾 ​

背景 🎯 ​

在前端开发中,当需要存储大量数据或二进制文件时,localStorage 的存储限制(通常为 5-10MB)往往无法满足需求。这时,IndexedDB 作为浏览器内置的低级 API 数据库,成为了最佳选择。

IndexedDB 核心特性 ⭐ ​

1. 基础特性 ​

  • 📦 键值对存储: 使用对象仓库(object store)存储数据,支持所有类型数据
  • 🔑 主键唯一: 每条数据都需要唯一主键标识
  • ⚡ 异步操作: 所有操作都是异步的,不会阻塞主线程
  • 🔄 事务机制: 支持事务操作,保证数据完整性
  • 🌐 同源策略: 遵循浏览器同源策略,只能访问同域名下的数据库
  • 💾 存储容量: 通常不少于 250MB,远超 localStorage
  • 📄 数据类型: 支持存储 JavaScript 对象、ArrayBuffer、Blob 等多种类型

2. 使用场景 🎨 ​

  • 离线应用数据存储
  • 大文件/二进制数据存储
  • 客户端缓存
  • 临时数据存储

实战指南 📝 ​

1. 数据库操作基础 ​

打开/创建数据库 ​

javascript
const dbName = 'MyDatabase';
const version = 1;
const request = indexedDB.open(dbName, version);

request.onerror = (event) => {
    console.error('数据库打开失败 ❌', event.target.error);
};

request.onsuccess = (event) => {
    const db = event.target.result;
    console.log('数据库打开成功 ✅');
};

request.onupgradeneeded = (event) => {
    const db = event.target.result;
    // 数据库升级或新建时的处理逻辑
};

创建对象仓库 ​

javascript
request.onupgradeneeded = (event) => {
    const db = event.target.result;
    
    // 创建对象仓库
    if (!db.objectStoreNames.contains('users')) {
        const store = db.createObjectStore('users', {
            keyPath: 'id',  // 指定主键
            autoIncrement: true  // 自动生成主键
        });
        
        // 创建索引
        store.createIndex('name', 'name', { unique: false });
        store.createIndex('email', 'email', { unique: true });
    }
};

2. 数据操作实用工具 🛠️ ​

以下是一个实用的 IndexedDB 操作封装:

javascript
// 数据库操作类
class IndexedDBHelper {
    static async openDB(dbName, storeName, version = 1) {
        return new Promise((resolve, reject) => {
            const request = indexedDB.open(dbName, version);
            
            request.onupgradeneeded = (event) => {
                const db = event.target.result;
                if (!db.objectStoreNames.contains(storeName)) {
                    db.createObjectStore(storeName, { autoIncrement: true });
                }
            };
            
            request.onsuccess = () => resolve(request.result);
            request.onerror = () => reject(request.error);
        });
    }

    static async putData(db, storeName, data) {
        return new Promise((resolve, reject) => {
            const transaction = db.transaction([storeName], 'readwrite');
            const store = transaction.objectStore(storeName);
            const request = store.put(data);
            
            request.onsuccess = () => resolve();
            request.onerror = () => reject(request.error);
        });
    }

    static async getData(db, storeName, key) {
        return new Promise((resolve, reject) => {
            const transaction = db.transaction([storeName], 'readonly');
            const store = transaction.objectStore(storeName);
            const request = store.get(key);
            
            request.onsuccess = () => resolve(request.result);
            request.onerror = () => reject(request.error);
        });
    }
}

最佳实践与注意事项 ⚠️ ​

  1. 版本控制

    • 每次修改数据库结构时都要更新版本号
    • 在 onupgradeneeded 中处理版本升级逻辑
  2. 错误处理

    • 始终添加错误处理逻辑
    • 使用 try-catch 包装异步操作
  3. 性能优化

    • 批量操作时使用同一个事务
    • 适当使用索引提升查询性能
  4. 存储限制

    • 定期清理不需要的数据
    • 监控存储空间使用情况

示例应用 🌟 ​

javascript
// 实际应用示例:存储用户上传的图片
async function saveUserImage(imageBlob) {
    try {
        const db = await IndexedDBHelper.openDB('ImageDB', 'images');
        await IndexedDBHelper.putData(db, 'images', {
            timestamp: Date.now(),
            data: imageBlob
        });
        console.log('图片保存成功 ✅');
    } catch (error) {
        console.error('图片保存失败 ❌', error);
    }
}

总结 📚 ​

IndexedDB 是一个功能强大的客户端存储解决方案,特别适合处理大量结构化数据。通过合理使用其事务和索引特性,可以构建高性能的离线应用。记住要处理好错误情况,并遵循最佳实践来优化应用性能。