indexdb增删改查以及导入导出

221 阅读3分钟

描述

常用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

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)
    })
  })
}