描述
常用web存储技术cookie/storage就不多描述了,直接进入主题:indexdb
特点
- 通过key-value键值对存储
- 异步
- 支持事务,新建一个事务,可以对db进行多次增删改操作,如果其中一次失败 的,当前事务下的所有库操作都会回滚
- 受同源策略限制
- 存储空间大,理论无上限
封装
-
注:这主要通过封装indexdb的增删改查熟悉indexdb的使用,下面的封装对数据操作,会失去的对事务的支持。
-
例:假设目前有两张表,表设计如下
- personnel表
- id 学生id
- name 学生姓名
- age 年龄
- gender 性别
- record表
- record_id 记录id
- user_id 学生id
- start_time 开始时间
- end_time 结束时间
- sport_type 运动类型 pingPong、basketball、badminton
- personnel表
index-db文件
新建index-db.ts文件,封装db操作方法
import { StoresNames, type StoreData, type InsertResp, type ExportData } from './index.type'
const SQL_NAME = 'myIndexDb' // 数据库名称
let db // 数据库对象
/**
* 初始化数据库数据表
*/
export function init(){}
// 查询数据记录
export function list(){}
// 查询数据库详情
export function detail(){}
// 添加数据
export function insert(){}
// 修改数据
export function update(){}
// 根据筛选条件删除数据
export function del(){}
// 数据库导出
export function exportIndexDb(){}
// 数据导入
export function importIndexDb(){}
新建index.type.ts文件,定义数据表的字段
// indexdb库
export enum StoresNames {
PERSONNEL = 'personnel',
RECORD = 'record'
}
// 数据操作函数封装的入参
export type StoreData = Personnel | Record
// 数据库导出的格式
export interface ExportData {
[StoresNames.PERSONNEL]?: Personnel[]
[StoresNames.RECORD]?: Record[]
}
// personnel表字段定义
export interface Personnel {
id?: number // 学生id,自增
name?: string // 学生姓名
age?: number
gender?: Gender
}
export interface Record {
id?: number // 记录id,自增
userId: number // 学生id
startTime?: number // 运动开始时间-时间戳
endTime?: number // 运动结束时间-时间戳
sportType?: SportType
}
// 性别
export enum Gender {}
// 乒乓、篮球、羽毛球
export enum SportType {}
// 增加数据后的返回结果
export interface InsertResp {
id: number
}
数据库数据表初始化
export function init() {
return new Promise((resolve, reject) => {
const request = indexedDB.open(SQL_NAME)
request.onerror = (e: any) => {
reject(e)
}
request.onsuccess = (e: any) => {
resolve(e.target.result)
db = e.target.result
}
request.onupgradeneeded = (e: any) => {
db = e.target.result
let objectStore
Object.values(StoresNames).forEach((store) => {
console.log(store)
if (!db.objectStoreNames.contains(store)) {
objectStore = db.createObjectStore(store, {
keyPath: 'id',
unique: true, // 唯一
autoIncrement: true // id自增
})
}
})
}
})
}
条件查询列表
- 筛选出于条件配置的列表
export function list(
store: StoresNames,
params: { [key: string]: any } = {}
): Promise<StoreData[]> {
return new Promise<StoreData[]>((resolve, reject) => {
const objectStore = db.transaction([store], 'readwrite').objectStore(store) // 新建一个事务
const res = objectStore.openCursor()
const result: StoreData[] = []
res.onsuccess = (e: any) => {
const cursor = e.target.result
if (cursor) {
// 改条数据是否满足所有过滤条件
const isMatch = Object.keys(params).every(
(key) => cursor.value[key]?.indexOf(params[key]) !== -1
)
if (isMatch) {
result.push(cursor.value)
}
cursor.continue()
} else {
resolve(result)
}
}
res.onerror = (e: any) => {
reject(e)
}
})
}
查询数据详情
- 根据数据表Key(id),查询数据详情
export function detail(store: StoresNames, id: number): Promise<StoreData> {
return new Promise<StoreData>((resolve, reject) => {
const objectStore = db.transaction([store], 'read').objectStore(store) // 新建一个事务
const res = objectStore.get(id)
res.onsuccess = (e: any) => {
resolve(e.target.result)
}
res.onerror = (e: any) => {
reject(e)
}
})
}
添加数据
export function insert(store: StoresNames, data: StoreData): Promise<InsertResp> {
return new Promise<InsertResp>((resolve, reject) => {
const objectStore = db.transaction([store], 'readwrite').objectStore(store) // 新建一个事务
const res = objectStore.add(data)
res.onsuccess = (e: any) => {
resolve(e.target.result)
}
res.onerror = (e: any) => {
reject(e)
}
})
}
更新数据
- 由于indexdb原生不支持更新数据表数据的其中某个字段的值,只能整个更新一条数据,所以这加了个先查再更的小逻辑,支持更新数据表中的其中某个字段
export function update(store: StoresNames, data: StoreData): Promise<any> {
return new Promise<any>((resolve, reject) => {
const objectStore = db.transaction([store], 'read').objectStore(store) // 新建一个事务
const { id } = data
const res = objectStore.get(id)
res.onsuccess = (e: any) => {
const detail = e.resolve()
const updateRes = objectStore.put({ ...detail, ...data })
updateRes.onsuccess = (e: any) => {
resolve(e.target.result)
}
updateRes.onerror = (e: any) => {
reject(e)
}
}
res.onerror = (e: any) => {
reject(e)
}
})
}
删除数据
export function del(store: StoresNames, id: number): Promise<any> {
return new Promise<any>(async (resolve, reject) => {
const objectStore = db.transaction([store], 'read').objectStore(store) // 新建一个事务
const res = objectStore.delete(id)
res.onsuccess = (e: any) => {
resolve(e.target.result)
}
res.onerror = (e: any) => {
reject(e)
}
})
}
导入导出
- 既然作为一个数据库,怎么能没有导入导出呢,必须支持
导出indexDb数据库
- 将indexDb数据库数据导出为json文件
export function exportIndexDb() {
const exportData: ExportData = {}
const promises: any[] = []
const storeNames = Object.values(StoresNames)
storeNames.forEach((store) => {
promises.push(store)
})
Promise.all(promises).then((data) => {
storeNames.forEach((store, index) => {
exportData[store] = data[index]
})
const exportJsonData = JSON.stringify(exportData)
const blob = new Blob([exportJsonData], { type: 'application/json' })
const a = document.createElement('a')
a.href = URL.createObjectURL(blob)
a.download = 'indexDbData.json'
a.click()
})
}
导入indexDb json数据
export function importIndexDb(exportData: ExportData) {
const storeNames = Object.values(StoresNames)
storeNames.forEach((store) => {
const objectStore = db.transaction([store], 'read').objectStore(store) // 新建一个事务
objectStore.clear() // 清除当前数据表的数据
exportData[store]?.forEach((item) => {
objectStore.add(item)
})
})
}