indexDB快速上手
目录
前言
打开 / 创建 数据库
添加数据
删除数据
更新数据
查询数据
按主键查询
范围查询(使用游标)
全部查询
cursor
getAll
前言
indexDB是浏览器内置的一种低级异步数据库,专为存储大量结构化数据而设计,属于HTML5标准的一部分。
它具备下面六个特性:
- 大容量存储:容量远远大于“localStorage”(通常为GB级别),适合存储大量数据
- 支持复杂数据类型:支持字符串、数字、对象、数组、Blob、File等
- 异步操作:所有操作均为异步,不会阻塞主线程,避免影响页面响应速度
- 事务支持:基于事务的原子操作,确保一组操作要么全部成功,要么全部失败,保证数据一致性
- 索引与查询能力:支持创建索引,可按非主键字段高效查询,支持范围查询
- 同源隔离策略支持:遵循浏览器同源策略,不同域名的页面 / 插件无法访问彼此的indexDB数据
它具备下面五个概念:
- 数据库:
- 每个数据库有唯一名称(字符串)和版本号(正整数)
- 版本号用于数据库结构升级(只能递增,不能递减)
- 对象仓库(表):
- 类似于关系数据库中的“表”,是存储数据的基本单元
- 每个对象仓库需指定主键,用于唯一标识数据
- 若数据无固定主键,可设置autoIncrement:true自动生成递增主键
- 索引:
- 基于对象仓库中的字段创建,用于加速查询
- 可指定是否唯一,避免重复值
- 事务:
- 所有数据操作(增删改查)必须在事务中执行
- 事务有三种模式:readonly(只读)、readwrite(读写)、versionchange(版本变更)
- 游标:
- 用于遍历对象仓库或索引中的数据,支持按条件查询和范围查询
如果您清楚数据库的相关概念,并且使用过相关数据库(MySQL、Oracle等等),相信indexDB对您来说小菜一碟
打开 / 创建 数据库
indexDB是“无模式”数据库,即不需要在数据库表中预先定义字段,这通常表现为:
- 我向user表添加一条记录:{name:"李华",age:12}
- 再向user表添加一条记录:{gender:"男"}
此时在数据库中结构如下:
name | age | gender |
---|---|---|
李华 | 12 | null |
null | null | 男 |
当再次添加表中没有的字段的记录时,indexDB会自动创建一个新的字段
export function createDB(){const request = indexedDB.open('extensionStudy',1)request.onupgradeneeded = (event) => {const db = event.target.result;// 若不存在users表,则创建user表if (!db.objectStoreNames.contains('users')){const userStore = db.createObjectStore("users",{keyPath:"id",autoIncrement:true})}}request.onsuccess = (event) => {const db = event.target.result;console.log("数据库打开成功",db)}request.onerror = (event) => {console.log("数据库打开失败",event.target.error)}console.log(request)
}
效果:
添加数据
request是store.add的一个回调结果,它包含下面的属性:
- error:出错的描述会被存放在这里
- onerror:出错时的回调
- onsuccess:成功时的回调
- readyState:执行状态
- result:添加数据的主键号
- source:来源
- transaction:事务
export function addUser(db,userData){// 开启读写事务const transaction = db.transaction(['users'],'readwrite')const store = transaction.objectStore('users')// 添加数据const request = store.add(userData)request.onsuccess = () => {console.log("数据添加成功",request.result)}request.onerror = (event) => {console.log("数据添加失败",event.target.error)}transaction.oncomplete = () => {console.log("事务完成")}
}
效果:
删除数据
删除数据时,只需要提供主键即可删除
export function deleteUser(db,id){// 开启读写事务const transaction = db.transaction(['users','readwrite'])const store = transaction.objectStore('users')const request = store.delete(id)request.onsuccess = () => {console.log("数据删除成功",request)}
}
效果:
更新数据
export function updateUser(db,userData){// 需包含主键(id),否则无法定位数据const transaction = db.transaction(["users"], "readwrite");const store = transaction.objectStore("users");// put() 方法:存在则更新,不存在则新增const request = store.put(userData);request.onsuccess = () => {console.log("数据更新成功");};
}
查询数据
按主键查询
export function getUserById(db,id){const transaction = db.transaction(['users'],'readonly')const store = transaction.objectStore('users')const request = store.get(id)console.log('结果',request)request.onsuccess =() => {const user = request.resultif (user){console.log("查询结果",user)}else{console.log("查询结果为空")}}
}
效果:
范围查询(使用游标)
export function getUsersByAgeRange(db,minAge,maxAge){const transaction = db.transaction(['users'],'readonly')const store = transaction.objectStore('users')// 打开游标const range = IDBKeyRange.bound(minAge,maxAge)const request = store.openCursor(range,"next")const results = []request.onsuccess = (event) => {const cursor = event.target.resultif (cursor){results.push(cursor.value)cursor.continue()}else{console.log("查询结果",results)}}
}
效果:
全部查询
获取所有数据可以使用cursor也可以使用getAll方法
cursor
cursor适合大数据量数据
function getAllDataByCursor(db, storeName) {return new Promise((resolve, reject) => {const result = []; // 存储所有数据// 1. 创建只读事务const transaction = db.transaction(storeName, "readonly");const store = transaction.objectStore(storeName);// 2. 打开游标(不指定范围,默认遍历所有数据)const request = store.openCursor();// 3. 游标打开成功request.onsuccess = (event) => {const cursor = event.target.result;if (cursor) {// 收集当前数据result.push(cursor.value);// 继续下一条cursor.continue();} else {// 游标为null,遍历完成resolve(result);}};// 4. 处理错误request.onerror = (event) => {console.error("游标查询失败:", event.target.error);reject(event.target.error);};});
}
getAll
getAll适合小数据量数据
function getAllDataByGetAll(db, storeName) {return new Promise((resolve, reject) => {// 1. 创建只读事务const transaction = db.transaction(storeName, "readonly");const store = transaction.objectStore(storeName);// 2. 调用 getAll() 获取所有数据(不传入参数表示获取全部)const request = store.getAll();// 3. 处理结果request.onsuccess = () => {resolve(request.result); // request.result 直接是所有数据的数组};request.onerror = (event) => {console.error("getAll 查询失败:", event.target.error);reject(event.target.error);};});
}