Web前端入门第 3 问:JavaScript 一个简单的 IndexedDB 数据库入门示例
一、什么是 IndexedDB?在Web开发中,我们经常需要在客户端存储数据。你可能已经熟悉了 localStorage 或 sessionStorage,但它们只能存储简单的字符串键值对,无法存储复杂的数据结构,而且存储容量有限(通常为5-10MB)。IndexedDB 是浏览器提供的一个本地数据库系统,它允许你在用户浏览器中存储大量结构化数据。与 localStorage 相比,IndexedDB 有以下优势:-存储容量大:通常可达几百MB甚至更多-支持复杂数据结构:可以存储对象、数组、二进制数据等-支持索引和查询:可以像 SQL 数据库一样进行条件查询-异步操作:不会阻塞页面渲染简单来说,IndexedDB 就是浏览器内置的“迷你数据库”,特别适合离线应用、数据缓存等场景。## 二、核心概念在开始写代码之前,我们需要理解几个关键概念:1.数据库(Database):最顶层的容器,一个域名下可以创建多个数据库2.对象仓库(Object Store):类似于关系数据库中的“表”,用于存储数据记录3.索引(Index):为对象仓库中的某个字段建立索引,方便快速查询4.事务(Transaction):所有读写操作都需要在事务中执行,确保数据一致性5.游标(Cursor):用于遍历对象仓库中的多条记录## 三、创建和打开数据库我们从一个最简单的示例开始:创建一个名为 “MyDatabase” 的数据库,并创建一个名为 “users” 的对象仓库。javascript// 步骤1:打开数据库const request = indexedDB.open("MyDatabase", 1);// 步骤2:处理打开失败的情况request.onerror = (event) => { console.error("数据库打开失败", event.target.error);};// 步骤3:处理打开成功的情况request.onsuccess = (event) => { const db = event.target.result; // 获取数据库实例 console.log("数据库打开成功!"); // 现在可以执行数据库操作了 // 但我们先关闭数据库,后面再详细操作 db.close();};// 步骤4:处理数据库版本升级(包括首次创建)request.onupgradeneeded = (event) => { const db = event.target.result; // 检查对象仓库是否已存在 if (!db.objectStoreNames.contains("users")) { // 创建对象仓库,并指定主键为 "id" const objectStore = db.createObjectStore("users", { keyPath: "id" }); // 为 "name" 字段创建索引,方便按名字查询 objectStore.createIndex("name", "name", { unique: false }); // 为 "email" 字段创建唯一索引 objectStore.createIndex("email", "email", { unique: true }); console.log("users 对象仓库创建成功!"); }};代码说明:-indexedDB.open()的第一个参数是数据库名称,第二个参数是版本号-onupgradeneeded事件会在数据库首次创建或版本号变更时触发-keyPath指定用哪个字段作为主键,类似 SQL 中的 PRIMARY KEY- 索引的unique参数表示是否允许重复值## 四、添加数据有了数据库结构后,我们来添加一些用户数据。javascript// 假设我们已经成功打开了数据库,db 是数据库实例function addUser(db, userData) { // 1. 创建一个事务,指定要操作的对象仓库和操作模式 // "readwrite" 表示读写模式 const transaction = db.transaction(["users"], "readwrite"); // 2. 获取对象仓库 const objectStore = transaction.objectStore("users"); // 3. 添加数据 const request = objectStore.add(userData); // 4. 处理成功/失败 request.onsuccess = (event) => { console.log("用户数据添加成功,主键为:", event.target.result); }; request.onerror = (event) => { console.error("添加用户失败:", event.target.error); }; // 5. 事务完成后的回调 transaction.oncomplete = () => { console.log("事务已完成"); };}// 调用示例:添加一个用户const user = { id: 1, name: "张三", email: "zhangsan@example.com", age: 25, createTime: new Date().toISOString()};// 注意:这里假设 db 是之前打开的数据库实例// 实际使用时需要确保 db 是有效的addUser(db, user);代码说明:- 每次操作都需要先创建事务transaction-add()方法用于添加新记录,如果主键已存在会报错- 如果想更新已存在的记录,可以使用put()方法## 五、查询数据### 5.1 通过主键查询javascriptfunction getUserById(db, id) { // 创建只读事务 const transaction = db.transaction(["users"], "readonly"); const objectStore = transaction.objectStore("users"); // 通过主键获取数据 const request = objectStore.get(id); request.onsuccess = (event) => { const user = event.target.result; if (user) { console.log("找到用户:", user); } else { console.log("未找到该用户"); } }; request.onerror = (event) => { console.error("查询失败:", event.target.error); };}// 调用示例:查询 id 为 1 的用户getUserById(db, 1);### 5.2 通过索引查询javascriptfunction getUserByEmail(db, email) { const transaction = db.transaction(["users"], "readonly"); const objectStore = transaction.objectStore("users"); // 获取 "email" 索引 const index = objectStore.index("email"); // 通过索引值查询 const request = index.get(email); request.onsuccess = (event) => { const user = event.target.result; if (user) { console.log("通过邮箱找到用户:", user); } else { console.log("未找到该邮箱对应的用户"); } };}// 调用示例getUserByEmail(db, "zhangsan@example.com");### 5.3 获取所有数据(使用游标)javascriptfunction getAllUsers(db) { const transaction = db.transaction(["users"], "readonly"); const objectStore = transaction.objectStore("users"); // 创建游标,遍历所有记录 const request = objectStore.openCursor(); const users = []; request.onsuccess = (event) => { const cursor = event.target.result; if (cursor) { // 将当前记录添加到数组 users.push(cursor.value); // 移动到下一条记录 cursor.continue(); } else { // 遍历完成 console.log("所有用户:", users); } };}// 调用示例getAllUsers(db);## 六、更新和删除数据### 6.1 更新数据javascriptfunction updateUser(db, id, newData) { const transaction = db.transaction(["users"], "readwrite"); const objectStore = transaction.objectStore("users"); // 先获取原数据 const getRequest = objectStore.get(id); getRequest.onsuccess = (event) => { const user = event.target.result; if (!user) { console.log("用户不存在,无法更新"); return; } // 合并新数据 const updatedUser = { ...user, ...newData }; // 使用 put 方法更新(如果主键存在则更新,不存在则新增) const putRequest = objectStore.put(updatedUser); putRequest.onsuccess = () => { console.log("用户更新成功"); }; };}// 调用示例:更新年龄updateUser(db, 1, { age: 26 });### 6.2 删除数据javascriptfunction deleteUser(db, id) { const transaction = db.transaction(["users"], "readwrite"); const objectStore = transaction.objectStore("users"); const request = objectStore.delete(id); request.onsuccess = () => { console.log("用户删除成功"); }; request.onerror = (event) => { console.error("删除失败:", event.target.error); };}// 调用示例deleteUser(db, 1);## 七、完整示例下面是一个完整的示例,展示如何从打开数据库到增删改查的全过程:javascript// 完整示例:一个简单的用户管理系统function initDatabase() { return new Promise((resolve, reject) => { const request = indexedDB.open("UserDatabase", 1); 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 }); } }; request.onsuccess = (event) => { resolve(event.target.result); }; request.onerror = (event) => { reject(event.target.error); }; });}// 使用示例async function main() { try { const db = await initDatabase(); console.log("数据库初始化成功"); // 添加用户 const user = { name: "李四", email: "lisi@example.com", age: 30 }; const transaction = db.transaction(["users"], "readwrite"); const store = transaction.objectStore("users"); store.add(user); // 查询所有用户 const allUsers = await new Promise((resolve) => { const users = []; const cursorRequest = store.openCursor(); cursorRequest.onsuccess = (event) => { const cursor = event.target.result; if (cursor) { users.push(cursor.value); cursor.continue(); } else { resolve(users); } }; }); console.log("所有用户:", allUsers); } catch (error) { console.error("操作失败:", error); }}main();## 八、总结通过本文的学习,我们掌握了 IndexedDB 的核心用法:1.数据库操作:使用indexedDB.open()创建或打开数据库2.对象仓库管理:在onupgradeneeded事件中创建和配置对象仓库3.数据增删改查:通过事务进行数据的添加、查询、更新和删除4.索引查询:利用索引实现高效的条件查询5.游标遍历:使用游标处理多条记录IndexedDB 虽然 API 看起来复杂,但它的设计模式非常清晰:先打开数据库 → 创建对象仓库 → 通过事务执行操作。只要掌握这个流程,你就能轻松地在浏览器中存储和管理复杂数据。在实际项目中,你可以将 IndexedDB 用于:- 离线 Web 应用的本地数据存储- 缓存服务器返回的 API 数据- 保存用户配置和偏好设置- 存储大型文件或媒体资源希望这篇文章能帮助你迈出 IndexedDB 学习的第一步!