【IndexDB】前端IndexedDB终极指南

前端 IndexedDB 详细教程

IndexedDB 是一个浏览器内置的 NoSQL 数据库系统,允许在客户端存储大量结构化数据,并支持高性能搜索。相比 localStorage,IndexedDB 更适合存储大量数据并提供更复杂的查询功能。

基本概念

  1. 数据库:每个源(协议+域名+端口)可以创建多个数据库
  2. 对象存储(Object Store):类似于数据库中的表
  3. 索引(Index):用于快速查找数据
  4. 事务(Transaction):所有操作必须在事务中执行
  5. 游标(Cursor):用于遍历对象存储中的数据

打开/创建数据库

const request = indexedDB.open('myDatabase', 1);

request.onerror = function(event) {
  console.error("数据库打开失败:", event.target.error);
};

request.onsuccess = function(event) {
  const db = event.target.result;
  console.log("数据库打开成功");
  // 在这里执行数据库操作
};

request.onupgradeneeded = function(event) {
  const db = event.target.result;
  // 创建对象存储和索引
  if (!db.objectStoreNames.contains('customers')) {
    const store = db.createObjectStore('customers', { keyPath: 'id' });
    // 创建索引
    store.createIndex('name', 'name', { unique: false });
    store.createIndex('email', 'email', { unique: true });
  }
};

添加数据

function addCustomer(db, customer) {
  const transaction = db.transaction(['customers'], 'readwrite');
  const store = transaction.objectStore('customers');
  
  const request = store.add(customer);
  
  request.onsuccess = function() {
    console.log('数据添加成功');
  };
  
  request.onerror = function(event) {
    console.error('数据添加失败:', event.target.error);
  };
}

// 使用示例
const db = /* 获取数据库实例 */;
addCustomer(db, {
  id: 1,
  name: '张三',
  email: '[email protected]',
  age: 30
});

读取数据

通过主键读取

function getCustomer(db, id) {
  const transaction = db.transaction(['customers'], 'readonly');
  const store = transaction.objectStore('customers');
  
  const request = store.get(id);
  
  request.onsuccess = function() {
    const customer = request.result;
    if (customer) {
      console.log('找到客户:', customer);
    } else {
      console.log('未找到客户');
    }
  };
  
  request.onerror = function(event) {
    console.error('读取数据失败:', event.target.error);
  };
}

通过索引读取

function getCustomerByName(db, name) {
  const transaction = db.transaction(['customers'], 'readonly');
  const store = transaction.objectStore('customers');
  const index = store.index('name');
  
  const request = index.get(name);
  
  request.onsuccess = function() {
    const customer = request.result;
    console.log('找到客户:', customer);
  };
  
  request.onerror = function(event) {
    console.error('通过索引查询失败:', event.target.error);
  };
}

更新数据

function updateCustomer(db, customer) {
  const transaction = db.transaction(['customers'], 'readwrite');
  const store = transaction.objectStore('customers');
  
  const request = store.put(customer);
  
  request.onsuccess = function() {
    console.log('数据更新成功');
  };
  
  request.onerror = function(event) {
    console.error('数据更新失败:', event.target.error);
  };
}

删除数据

function deleteCustomer(db, id) {
  const transaction = db.transaction(['customers'], 'readwrite');
  const store = transaction.objectStore('customers');
  
  const request = store.delete(id);
  
  request.onsuccess = function() {
    console.log('数据删除成功');
  };
  
  request.onerror = function(event) {
    console.error('数据删除失败:', event.target.error);
  };
}

使用游标遍历数据

function getAllCustomers(db) {
  const transaction = db.transaction(['customers'], 'readonly');
  const store = transaction.objectStore('customers');
  
  const request = store.openCursor();
  const customers = [];
  
  request.onsuccess = function(event) {
    const cursor = event.target.result;
    if (cursor) {
      customers.push(cursor.value);
      cursor.continue();
    } else {
      console.log('所有客户:', customers);
    }
  };
  
  request.onerror = function(event) {
    console.error('遍历数据失败:', event.target.error);
  };
}

高级用法

使用索引范围查询

function getCustomersByAgeRange(db, min, max) {
  const transaction = db.transaction(['customers'], 'readonly');
  const store = transaction.objectStore('customers');
  
  // 假设我们创建了age索引
  const index = store.index('age');
  const range = IDBKeyRange.bound(min, max);
  
  const request = index.openCursor(range);
  const customers = [];
  
  request.onsuccess = function(event) {
    const cursor = event.target.result;
    if (cursor) {
      customers.push(cursor.value);
      cursor.continue();
    } else {
      console.log('年龄范围内的客户:', customers);
    }
  };
}

批量操作

function addMultipleCustomers(db, customers) {
  const transaction = db.transaction(['customers'], 'readwrite');
  const store = transaction.objectStore('customers');
  
  customers.forEach(customer => {
    store.add(customer);
  });
  
  transaction.oncomplete = function() {
    console.log('批量添加完成');
  };
  
  transaction.onerror = function(event) {
    console.error('批量操作失败:', event.target.error);
  };
}

数据库版本管理

// 升级数据库版本
function upgradeDB() {
  const request = indexedDB.open('myDatabase', 2); // 版本号增加
  
  request.onupgradeneeded = function(event) {
    const db = event.target.result;
    const oldVersion = event.oldVersion;
    const newVersion = event.newVersion;
    
    // 从版本1升级到版本2
    if (oldVersion < 1) {
      // 初始创建逻辑
    }
    
    if (oldVersion < 2) {
      // 版本2的变更
      if (!db.objectStoreNames.contains('orders')) {
        const store = db.createObjectStore('orders', { keyPath: 'orderId' });
        store.createIndex('customerId', 'customerId', { unique: false });
      }
    }
  };
}

最佳实践

  1. 错误处理:始终处理onerror事件
  2. 事务管理:合理使用事务,避免长时间持有事务
  3. 性能优化:对于大量数据操作,使用游标分批处理
  4. 内存管理:处理完数据后关闭游标和数据库连接
  5. 兼容性:检查浏览器支持情况
if (!window.indexedDB) {
  console.error("您的浏览器不支持IndexedDB");
}

封装示例

class IndexedDBWrapper {
  constructor(dbName, version) {
    this.dbName = dbName;
    this.version = version;
    this.db = null;
  }
  
  open() {
    return new Promise((resolve, reject) => {
      const request = indexedDB.open(this.dbName, this.version);
      
      request.onerror = (event) => reject(event.target.error);
      
      request.onsuccess = (event) => {
        this.db = event.target.result;
        resolve(this.db);
      };
      
      request.onupgradeneeded = (event) => {
        const db = event.target.result;
        if (!db.objectStoreNames.contains('data')) {
          const store = db.createObjectStore('data', { keyPath: 'id' });
          store.createIndex('timestamp', 'timestamp', { unique: false });
        }
      };
    });
  }
  
  add(storeName, data) {
    return new Promise((resolve, reject) => {
      const transaction = this.db.transaction([storeName], 'readwrite');
      const store = transaction.objectStore(storeName);
      
      const request = store.add(data);
      
      request.onsuccess = () => resolve(request.result);
      request.onerror = (event) => reject(event.target.error);
    });
  }
  
  // 其他方法类似封装...
}

// 使用示例
(async function() {
  const dbWrapper = new IndexedDBWrapper('myAppDB', 1);
  try {
    await dbWrapper.open();
    await dbWrapper.add('data', { id: 1, value: 'test', timestamp: Date.now() });
    console.log('操作成功');
  } catch (error) {
    console.error('操作失败:', error);
  }
})();

IndexedDB 提供了强大的客户端存储能力,适合需要离线功能或处理大量结构化数据的 Web 应用。通过合理使用,可以显著提升应用性能和用户体验。

你可能感兴趣的:(前端,前端)