====== 第二部分:环境搭建与第一个数据库 ====== ===== 2.1 开发环境准备 ===== ===== 2.1.1 浏览器支持情况 ===== 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; } ===== 2.1.2 开发工具 ===== **浏览器开发者工具** 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:代码片段 ===== 2.2 第一个 IndexedDB 应用 ===== ===== 2.2.1 打开数据库 ===== 使用 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('数据库被阻塞,请关闭其他标签页'); }; ===== 2.2.2 理解版本控制 ===== 版本号是 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 }); } }; ===== 2.2.3 创建对象存储空间 ===== 在 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'] }); }; ===== 2.2.4 添加第一条数据 ===== 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(); // 关闭数据库连接 }; }; ===== 2.3 完整的入门示例 ===== ===== 2.3.1 用户管理示例 ===== 一个完整的用户管理数据库示例: 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(); })(); ===== 2.3.2 错误处理最佳实践 ===== 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); } } ===== 2.4 数据库连接管理 ===== ===== 2.4.1 连接生命周期 ===== 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(); } } ===== 2.5 本章小结 ===== 本章介绍了 IndexedDB 的环境搭建和基础使用: * 所有现代浏览器都支持 IndexedDB * 使用 indexedDB.open() 打开数据库 * 在 onupgradeneeded 中创建对象存储和索引 * 所有操作通过事务执行 * 建议对原生 API 进行 Promise 封装 继续阅读 [[part03-objectstore|第三部分:对象存储空间详解]]。