IndexedDB:一个强大的客户端存储解决方案

引言

在现代 Web 开发中,客户端存储变得越来越重要。随着 Web 应用变得越来越复杂,开发者需要能够在用户的浏览器中存储大量结构化数据的能力。IndexedDB 正是为解决这一问题而生的浏览器内置数据库系统。本文将深入探讨 IndexedDB 的特性、使用场景以及实际应用方法。

什么是IndexedDB?

IndexedDB(Indexed Database API)是浏览器提供的一种底层 API,用于在客户端存储大量结构化数据。它不同于我们熟悉的 localStorage,IndexedDB 提供了更接近传统数据库的功能,包括索引、事务处理和大容量存储。

IndexedDB的核心特点

  1. 键值对存储:数据以对象仓库(object store)形式组织,所有类型的数据都可以直接存入,包括 JavaScript 对象。每一个数据记录都有对应的主键,主键是独一无二的,不能有重复,否则会抛出一个错误。
  2. 异步操作:所有操作都是异步的,不会阻塞UI线程
  3. 事务支持:支持ACID事务,保证数据完整性
  4. 大容量存储:通常至少250MB,没有明确上限
  5. 同源限制:遵循同源策略,保障数据安全。也就是说IndexedDB 受到同源限制,每一个数据库对应创建它的域名。网页只能访问自身域名下的数据库,而不能访问跨域的数据库。
  6. 二进制支持:可以存储ArrayBuffer和Blob对象

为什么选择 IndexedDB?

当你的应用需要:

  • 存储大量客户端数据
  • 需要高性能的搜索和查询
  • 处理复杂的数据结构
  • 需要事务支持保证数据一致性
  • 存储二进制数据(如图片、文件等)

IndexedDB就是理想的选择。相比 Web SQL(已废弃)和 localStorage,IndexedDB 提供了更强大和灵活的功能。

IndexedDB基础使用

1. 打开/创建数据库

let db;
const request = indexedDB.open("myDatabase", 1);
request.onerror = function (event) {
    console.error("Database error:", event.target.error);
};
request.onsuccess = function (event) {
    db = event.target.result;
    console.log("Database opened successfully");
};
request.onupgradeneeded = function (event) {
    const db = event.target.result;
    if (!db.objectStoreNames.contains("users")) {
        db.createObjectStore("users", { keyPath: "id", autoIncrement: true });
    }
};

当该数据库不存在时,open 方法会直接创建一个名为 myDatabase 新数据库

2. 添加数据

function addUser(db, user) {
    const transaction = db.transaction(["users"], "readwrite");
    const store = transaction.objectStore("users");
    const request = store.add(user);
    request.onsuccess = function () {
        console.log("User added");
    };
    request.onerror = function (event) {
        console.error("Error adding user:", event.target.error);
    };
}

3. 查询数据

function getUser(db, id) {
    const transaction = db.transaction(["users"], "readonly");
    const store = transaction.objectStore("users");
    const request = store.get(id);
    request.onsuccess = function () {
        if (request.result) {
            console.log("User:", request.result);
        } else {
            console.log("User not found");
        }
    };
    request.onerror = function (event) {
        console.error("Error getting user:", event.target.error);
    };
}

高级特性

索引和高效查询

IndexedDB 允许创建索引,实现高效的数据查询:

request.onupgradeneeded = function(event) {
    const db = event.target.result;
    const store = db.createObjectStore('users', { keyPath: 'id' });
    store.createIndex('email', 'email', { unique: true });
    store.createIndex('name', 'name', { unique: false });
};

// 使用索引查询
function getUserByEmail(db, email) {
    const transaction = db.transaction(['users'], 'readonly');
    const store = transaction.objectStore('users');
    const index = store.index('email');

    const request = index.get(email);

    request.onsuccess = function() {
        console.log('User by email:', request.result);
    };

游标和范围查询

function getUsersInRange(db, minId, maxId) {
    const transaction = db.transaction(["users"], "readonly");
    const store = transaction.objectStore("users");

    const range = IDBKeyRange.bound(minId, maxId);
    const request = store.openCursor(range);

    request.onsuccess = function (event) {
        const cursor = event.target.result;
        if (cursor) {
            console.log("User:", cursor.value);
            cursor.continue();
        }
    };
}

常用工具库推荐

由于原生IndexedDB API较为底层,推荐使用这些库简化开发:

  1. Dexie.js:轻量级IndexedDB封装,提供更简洁的API
  2. localForage:提供类似localStorage的简单API,但底层使用IndexedDB
  3. PouchDB:支持与CouchDB同步的客户端数据库

示例:使用Dexie.js简化代码

const db = new Dexie('myDatabase');

db.version(1).stores({
    users: '++id, name, email',
    posts: '++id, title, content, userId'
});

// 添加用户
db.users.add({ name: 'John', email: 'john@example.com' })
    .then(() => console.log('User added'))
    .catch(err => console.error('Error:', err));

// 查询
db.users.where('name').equals('John').toArray()
    .then(users => console.log('Found users:', users));