indexeddb:part02-setup

第二部分:环境搭建与第一个数据库

IndexedDB 在所有现代浏览器中都得到了良好支持:

浏览器 最低版本 备注
Chrome 23+ 完全支持
Firefox 16+ 完全支持
Safari 10+ 完全支持
Edge 12+ 完全支持
Opera 15+ 完全支持
iOS Safari 10.3+ 完全支持
Android Chrome 25+ 完全支持

检测 IndexedDB 支持:

function isIndexedDBSupported() {
  return 'indexedDB' in window && 
         indexedDB !== null && 
         indexedDB !== undefined;
}
 
// 更详细的检测
function getIndexedDBInfo() {
  const info = {
    supported: false,
    vendor: 'unknown',
    estimatedQuota: null
  };
 
  if (!isIndexedDBSupported()) {
    return info;
  }
 
  info.supported = true;
 
  // 检测浏览器类型
  const ua = navigator.userAgent;
  if (ua.includes('Chrome')) info.vendor = 'chrome';
  else if (ua.includes('Firefox')) info.vendor = 'firefox';
  else if (ua.includes('Safari')) info.vendor = 'safari';
  else if (ua.includes('Edge')) info.vendor = 'edge';
 
  // 检测存储配额
  if (navigator.storage && navigator.storage.estimate) {
    navigator.storage.estimate().then(estimate => {
      info.estimatedQuota = estimate.quota;
    });
  }
 
  return info;
}

浏览器开发者工具

Chrome/Edge 开发者工具中查看 IndexedDB:

  • 打开 DevTools(F12)
  • 选择 Application(应用)标签
  • 左侧菜单选择 IndexedDB
  • 可以查看数据库、对象存储空间、索引和数据

Firefox 开发者工具:

  • 打开 DevTools
  • 选择 Storage(存储)标签
  • 选择 IndexedDB 部分

Safari 开发者工具:

  • 需要先启用 Develop 菜单
  • Preferences → Advanced → Show Develop menu
  • Develop → Show Web Inspector

推荐的 VS Code 插件:

  • ESLint:代码检查
  • Prettier:代码格式化
  • JavaScript (ES6) code snippets:代码片段

使用 indexedDB.open() 方法打开或创建数据库:

// 基本语法
const request = indexedDB.open(databaseName, version);
 
// 示例
const request = indexedDB.open('MyFirstDatabase', 1);
 
request.onsuccess = function(event) {
  const db = event.target.result;
  console.log('数据库打开成功');
  console.log('数据库名称:', db.name);
  console.log('数据库版本:', db.version);
};
 
request.onerror = function(event) {
  console.error('数据库打开失败:', event.target.error);
};
 
request.onupgradeneeded = function(event) {
  const db = event.target.result;
  console.log('数据库需要升级');
  // 在这里创建对象存储和索引
};
 
request.onblocked = function(event) {
  console.warn('数据库被阻塞,请关闭其他标签页');
};

版本号是 IndexedDB 中非常关键的概念:

// 场景一:首次创建数据库(版本 1)
const request1 = indexedDB.open('MyApp', 1);
request1.onupgradeneeded = function(event) {
  const db = event.target.result;
  // 首次创建对象存储
  if (!db.objectStoreNames.contains('users')) {
    db.createObjectStore('users', { keyPath: 'id' });
  }
};
 
// 场景二:升级数据库(版本 2)
const request2 = indexedDB.open('MyApp', 2);
request2.onupgradeneeded = function(event) {
  const db = event.target.result;
  const oldVersion = event.oldVersion;
  const newVersion = event.newVersion;
 
  console.log(`从版本 ${oldVersion} 升级到 ${newVersion}`);
 
  // 根据旧版本执行不同的升级逻辑
  if (oldVersion < 2) {
    // 添加新的索引
    const store = request2.transaction.objectStore('users');
    store.createIndex('emailIndex', 'email', { unique: true });
  }
};

在 onupgradeneeded 回调中创建对象存储:

request.onupgradeneeded = function(event) {
  const db = event.target.result;
 
  // 方法 1:使用 keyPath(内联键)
  const store1 = db.createObjectStore('users', { 
    keyPath: 'userId' 
  });
 
  // 方法 2:使用 autoIncrement(自增键)
  const store2 = db.createObjectStore('logs', { 
    autoIncrement: true 
  });
 
  // 方法 3:同时指定 keyPath 和 autoIncrement
  const store3 = db.createObjectStore('orders', { 
    keyPath: 'orderId',
    autoIncrement: true 
  });
 
  // 方法 4:复合主键
  const store4 = db.createObjectStore('memberships', {
    keyPath: ['userId', 'groupId']
  });
};
request.onsuccess = function(event) {
  const db = event.target.result;
 
  // 开启读写事务
  const transaction = db.transaction(['users'], 'readwrite');
  const store = transaction.objectStore('users');
 
  // 添加数据
  const addRequest = store.add({
    userId: 1,
    name: '张三',
    email: 'zhangsan@example.com',
    age: 25,
    createdAt: new Date()
  });
 
  addRequest.onsuccess = function() {
    console.log('数据添加成功,主键:', addRequest.result);
  };
 
  addRequest.onerror = function() {
    console.error('添加失败:', addRequest.error);
  };
 
  // 事务完成
  transaction.oncomplete = function() {
    console.log('事务完成,数据已持久化');
    db.close(); // 关闭数据库连接
  };
};

一个完整的用户管理数据库示例:

class UserDatabase {
  constructor(dbName = 'UserDB') {
    this.dbName = dbName;
    this.db = null;
  }
 
  // 打开数据库
  open() {
    return new Promise((resolve, reject) => {
      const request = indexedDB.open(this.dbName, 1);
 
      request.onupgradeneeded = (event) => {
        const db = event.target.result;
 
        // 创建用户对象存储
        if (!db.objectStoreNames.contains('users')) {
          const userStore = db.createObjectStore('users', { 
            keyPath: 'id', 
            autoIncrement: true 
          });
 
          // 创建索引
          userStore.createIndex('nameIndex', 'name', { unique: false });
          userStore.createIndex('emailIndex', 'email', { unique: true });
          userStore.createIndex('ageIndex', 'age', { unique: false });
        }
      };
 
      request.onsuccess = (event) => {
        this.db = event.target.result;
        resolve(this.db);
      };
 
      request.onerror = (event) => {
        reject(event.target.error);
      };
    });
  }
 
  // 添加用户
  addUser(user) {
    return new Promise((resolve, reject) => {
      const transaction = this.db.transaction(['users'], 'readwrite');
      const store = transaction.objectStore('users');
      const request = store.add(user);
 
      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
  }
 
  // 获取用户
  getUser(id) {
    return new Promise((resolve, reject) => {
      const transaction = this.db.transaction(['users'], 'readonly');
      const store = transaction.objectStore('users');
      const request = store.get(id);
 
      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
  }
 
  // 关闭数据库
  close() {
    if (this.db) {
      this.db.close();
      this.db = null;
    }
  }
}
 
// 使用示例
(async function() {
  const userDB = new UserDatabase();
  await userDB.open();
 
  // 添加用户
  const userId = await userDB.addUser({
    name: '张三',
    email: 'zhangsan@example.com',
    age: 25
  });
  console.log('添加用户,ID:', userId);
 
  // 获取用户
  const user = await userDB.getUser(userId);
  console.log('用户信息:', user);
 
  userDB.close();
})();
function handleIndexedDBError(error) {
  switch (error.name) {
    case 'QuotaExceededError':
      console.error('存储配额不足');
      // 提示用户清理数据或使用持久存储
      break;
 
    case 'VersionError':
      console.error('版本号错误');
      // 检查版本号逻辑
      break;
 
    case 'NotFoundError':
      console.error('对象存储不存在');
      // 检查数据库结构
      break;
 
    case 'InvalidStateError':
      console.error('数据库连接已关闭');
      // 重新打开数据库
      break;
 
    case 'AbortError':
      console.error('事务被中止');
      // 检查事务逻辑
      break;
 
    default:
      console.error('未知错误:', error);
  }
}
class DBConnectionManager {
  constructor() {
    this.connections = new Map();
  }
 
  // 获取或创建连接
  async getConnection(dbName, version) {
    if (this.connections.has(dbName)) {
      const conn = this.connections.get(dbName);
      if (conn.readyState === 'done') {
        return conn;
      }
    }
 
    const db = await this.openConnection(dbName, version);
    this.connections.set(dbName, db);
    return db;
  }
 
  openConnection(dbName, version) {
    return new Promise((resolve, reject) => {
      const request = indexedDB.open(dbName, version);
      request.onsuccess = (e) => resolve(e.target.result);
      request.onerror = (e) => reject(e.target.error);
    });
  }
 
  // 关闭指定连接
  closeConnection(dbName) {
    if (this.connections.has(dbName)) {
      this.connections.get(dbName).close();
      this.connections.delete(dbName);
    }
  }
 
  // 关闭所有连接
  closeAll() {
    this.connections.forEach((db, name) => {
      db.close();
      console.log(`关闭数据库连接: ${name}`);
    });
    this.connections.clear();
  }
}

本章介绍了 IndexedDB 的环境搭建和基础使用:

  • 所有现代浏览器都支持 IndexedDB
  • 使用 indexedDB.open() 打开数据库
  • 在 onupgradeneeded 中创建对象存储和索引
  • 所有操作通过事务执行
  • 建议对原生 API 进行 Promise 封装

继续阅读 第三部分:对象存储空间详解

该主题尚不存在

您访问的页面并不存在。如果允许,您可以使用创建该页面按钮来创建它。

  • indexeddb/part02-setup.txt
  • 最后更改: 2026/04/27 19:50
  • 张叶安