Skip to content

indexDB

IndexDB 是一个运行在浏览器上的非关系型数据库。理论上是没有存储上限的一般来说不会小于 250M)。它不仅可以存储字符串,还可以存储二进制数据。

特点:

  • 键值对储存 所有类型的数据都可以直接存入,包括 JavaScript 对象。对象仓库中,数据以"键值对"的形式保存,每一个数据记录都有对应的主键,主键是独一无二的,不能有重复,否则会抛出一个错误。

  • 异步 IndexedDB 操作时不会锁死浏览器,用户依然可以进行其他操作,这与 LocalStorage 形成对比,后者的操作是同步的。异步设计是为了防止大量数据的读写,拖慢网页的表现。

  • 同源限制 每一个数据库对应创建它的域名。网页只能访问自身域名下的数据库,而不能访问跨域的数据库。

  • 支持二进制储存

  • 储存空间大

idb 库

reference

github

  • 安装
shell
pnpm add idb
  • 示例
ts
import { openDB, type DBSchema } from 'idb'

type Schema<T extends DBSchema> = {
    [P in keyof T]: T[P]['indexes'] extends Recordable
        ? {
              indexes: (keyof T[P]['indexes'])[]
          }
        : object
}

interface Config<T extends DBSchema> {
    SCHEMA: Schema<T>
    DB_NAME: string
    DB_VERSION: number
}
// 1. 先定义数据库类型
interface MyDB extends DBSchema {
    todo: {
        key: string
        value: number
    }
    dictionary: {
        value: {
            label: string
            meaning: string
            level: 'cet4' | 'cet6' | 'high_school'
        }
        key: number // 主索引
        indexes: { label: string }
    }
}
// 2. 定义完整信息
const DB_CONFIG: Config<MyDB> = {
    SCHEMA: {
        dictionary: {
            indexes: ['level'],
        },
        todo: {},
    },
    DB_NAME: 'my-db',
    DB_VERSION: 1,
}
export async function demo() {
    const { DB_NAME, DB_VERSION, SCHEMA } = DB_CONFIG
    // 3. 赋值即可
    const db = await openDB<MyDB>(DB_NAME, DB_VERSION, {
        upgrade(db) {
            /*
             * 根据 SCHEMA 创建对应的表
             * indexes 存在的话,就创建对应的索引;
             * value 为对象类型,默认使用 id 作为主键(自增)
             *
             */
            const createdStoreKeys = Object.values(db.objectStoreNames)
            const storeKeys = Object.keys(SCHEMA) as typeof createdStoreKeys
            const unCreate = storeKeys.filter((store) => !createdStoreKeys.includes(store))
            if (unCreate.length) {
                unCreate.forEach((store) => {
                    const indexes = (Reflect.get(SCHEMA, store) as Recordable)['indexes'] as
                        | null
                        | string[]
                    const s = db.createObjectStore(
                        store,
                        indexes
                            ? {
                                  keyPath: 'id',
                                  autoIncrement: true,
                              }
                            : undefined
                    )

                    if (indexes) {
                        // eslint-disable-next-line @typescript-eslint/ban-ts-comment
                        // @ts-expect-error
                        indexes.forEach((index) => s.createIndex(index, index))
                    }
                })
            }
        },
    })

    // value 是对象,那么就可以不传递 key 属性
    db.put('dictionary', {
        label: 'hi',
        meaning: '你好2',
        level: 'cet4',
    })
    // value 是 number,需要传递对应的 key(唯一),相同的时候就是更新
    db.put('todo', 1, 'someKey')
    // 根据索引查询(从 dictionary 中查找索引 level = cet4)
    db.getAllFromIndex('dictionary', 'level', 'cet4')
    // 根据主索引查询(id=1)
    db.get('dictionary', 1)
    // 根据主索引查询(附加 id >= 2 条件)
    db.getAll('dictionary', IDBKeyRange.lowerBound(2))
    // 删除
    db.delete('dictionary', IDBKeyRange.lowerBound(5))
}