IndexedDB 整理笔记

401 阅读15分钟

基本概念

IndexedDB的兼容性

特性

  • 可以存储大量数据(>250MB,2G+)
  • 可以离线查询(离线应用)
  • 可以持久化的存储数据(长久保留)
  • 可以在Web Worker中使用(多线程)
  • 基于JS的面向对象的数据库
  • 事务模式的数据库
  • 使用键值对存储数据
  • 异步API和事件通知
  • 创建索引
  • 使用游标遍历数据集合
  • 存储结构化克隆算法支持的对象
  • 浏览器的同源策略

术语

developer.mozilla.org/zh-CN/docs/…

  • 数据库
    • 版本号
    • 对象仓库
    • 事务
    • 索引
    • 请求
  • 键和值
    • 键路径
    • 键生成器
    • 外键
  • 作用域和范围
    • 只读和读写
    • 游标
    • 键范围

基本模式

developer.mozilla.org/zh-CN/docs/…

  1. 打开数据库
  2. 创建对象仓库
  3. 启动事务,请求数据库操作
  4. 监听请求事件,等待操作完成
  5. 在操作结果上进行一些操作

对象接口

  • IDBOpenDBRequest 打开数据库的请求
  • IDBDatabase 数据库连接的实例
  • IDBTransaction 事务
  • IDBObjectStore 对象仓库
  • IDBIndex 索引
  • IDBCursor 游标 (IDBCursorWithValue)
  • IDBKeyRange 键范围
  • IDBRequest 处理数据库的请求

具体操作

数据库

新建、升级、连接数据库

indexedDB.open(name: string, version?: number): IDBOpenDBRequest
const openReq = indexedDB.open('test') // version 默认为 1
openReq.onupgradeneeded = (e) => {
  console.log('open onupgradeneeded', e) // e: IDBVersionChangeEvent
  // 创建(删除)对象仓库,以及创建(删除)索引
}
openReq.onsuccess = (e) => {
  console.log('open onsuccess', e) // e: Event
  // 获取数据库对象实例,为数据的增删改查做准备
  const db = openReq.result
}

关闭数据库

IDBDatabase.close(): void
const openReq = indexedDB.open('test')
openReq.onsuccess = () => {
  const db = openReq.result
  // ...
  db.close()
}

删除数据库

indexedDB.deleteDatabase(name: string): IDBOpenDBRequest
const delReq = indexedDB.deleteDatabase('test')
delReq.onsuccess = (e) => {
  console.log('del onsuccess', e) // e: IDBVersionChangeEvent
}

对象仓库

新建对象仓库

IDBDatabase.createObjectStore(name: string, optionalParameters?: IDBObjectStoreParameters): IDBObjectStore
键路径(keyPath) 键生成器(autoIncrement) 描述
No No 这种对象存储空间可以持有任意类型的值,甚至是像数字和字符串这种基本数据类型的值。每当我们想要增加一个新值的时候,必须提供一个单独的键参数。
Yes No 这种对象存储空间只能持有 JavaScript 对象。这些对象必须具有一个和 key path 同名的属性。
No Yes 这种对象存储空间可以持有任意类型的值。键会为我们自动生成,或者如果你想要使用一个特定键的话你可以提供一个单独的键参数。
Yes Yes 这种对象存储空间只能持有 JavaScript 对象。通常一个键被生成的同时,生成的键的值被存储在对象中的一个和 key path 同名的属性中。然而,如果这样的一个属性已经存在的话,这个属性的值被用作键而不会生成一个新的键。

使用指定键值

  • 创建并访问对象仓库
const openReq = indexedDB.open('test', 2)
openReq.onupgradeneeded = () => {
  const db = openReq.result
  const store = db.createObjectStore('users_out') // key 外键
}
openReq.onsuccess = () => {
  const db = openReq.result
  const tx = db.transaction(['users_out']) // mode 默认为 'readonly'
  tx.oncomplete = (e) => {
    console.log('tx oncomplete', e)
  }
  tx.onerror = (e) => {
    console.log('tx onerror', e)
  }
  const store = tx.objectStore('users_out')
  console.log('store', store.name)
}
  • 新增数据
const openReq = indexedDB.open('test', 2)
openReq.onsuccess = () => {
  const db = openReq.result
  const tx = db.transaction(['users_out'], 'readwrite')
  tx.oncomplete = (e) => {
    console.log('tx oncomplete', e)
  }
  tx.onerror = (e) => {
    console.log('tx onerror', e)
  }
  const store = tx.objectStore('users_out')
  const req = store.add({ name: '小王' }, '1') // Key: 1; Value: { name: "小王" }
  req.onsuccess = () => {
    console.log('store onsuccess', req.result)
  }
  req.onerror = (e) => {
    console.log('store onerror', e)
  }
}

使用键生成器

const openReq = indexedDB.open('test', 3)
openReq.onupgradeneeded = () => {
  const db = openReq.result
  db.createObjectStore('users_gen', { autoIncrement: true })
}
openReq.onsuccess = () => {
  const db = openReq.result
  const tx = db.transaction(['users_gen'], 'readwrite')
  tx.oncomplete = (e) => {
    console.log('tx oncomplete', e)
  }
  tx.onerror = (e) => {
    console.log('tx onerror', e)
  }
  const store = tx.objectStore('users_gen')
  const req = store.add({ name: '小王' }) // Key: 1; Value: { name: "小王" }
  req.onsuccess = () => {
    console.log('store onsuccess', req.result)
  }
  req.onerror = (e) => {
    console.log('store onerror', e)
  }
  store.add({ name: '小王' }) // Key: 2; Value: { name: "小王" }
}

使用键路径(属性)

const openReq = indexedDB.open('test', 4)
openReq.onupgradeneeded = () => {
  const db = openReq.result
  db.createObjectStore('users_id', { keyPath: 'id' })
}
openReq.onsuccess = () => {
  const db = openReq.result
  const tx = db.transaction(['users_id'], 'readwrite')
  tx.oncomplete = (e) => {
    console.log('tx oncomplete', e)
  }
  tx.onerror = (e) => {
    console.log('tx onerror', e)
  }
  const store = tx.objectStore('users_id')
  const req = store.add({ id: 'xw', name: '小王' }) // Key(key path: "id"): "xw"; Value: { id: "xw", name: "小王" }
  req.onsuccess = () => {
    console.log('store onsuccess', req.result)
  }
  req.onerror = (e) => {
    console.log('store onerror', e)
  }
}

使用键路径(点号连接)

const openReq = indexedDB.open('test', 5)
openReq.onupgradeneeded = () => {
  const db = openReq.result
  db.createObjectStore('users_point', { keyPath: 'user.id' })
}
openReq.onsuccess = () => {
  const db = openReq.result
  const tx = db.transaction(['users_point'], 'readwrite')
  tx.oncomplete = (e) => {
    console.log('tx oncomplete', e)
  }
  tx.onerror = (e) => {
    console.log('tx onerror', e)
  }
  const store = tx.objectStore('users_point')
  const req = store.add({ user: { id: '11', name: '小王' } }) // Key: "11"; Value: { user: {id: "11", name: "小王"} }
  req.onsuccess = () => {
    console.log('store onsuccess', req.result)
  }
  req.onerror = (e) => {
    console.log('store onerror', e)
  }
  store.add({ user: { id: '22', name: '小王' } }) // Key: "22"; Value: { user: {id: "22", name: "小王"} }
}

使用键路径(数组)

const openReq = indexedDB.open('test', 6)
openReq.onupgradeneeded = () => {
  const db = openReq.result
  db.createObjectStore('users_arr', { keyPath: ['id', 'name'] })
}
openReq.onsuccess = () => {
  const db = openReq.result
  const tx = db.transaction(['users_arr'], 'readwrite')
  tx.oncomplete = (e) => {
    console.log('tx oncomplete', e)
  }
  tx.onerror = (e) => {
    console.log('tx onerror', e)
  }
  const store = tx.objectStore('users_arr')
  const req = store.add({ id: '11', name: '小王' }) // Key: ["11", "小王"]; Value: {id: "11", name: "小王"}
  req.onsuccess = () => {
    console.log('store onsuccess', req.result)
  }
  req.onerror = (e) => {
    console.log('store onerror', e)
  }
  store.add({ id: '11', name: '小张' }) // Key: ["11", "小张"]; Value: {id: "11", name: "小张"}
  store.add({ id: '22', name: '小王' }) // Key: ["22", "小王"];; Value: {id: "22", name: "小王"}
}

同时使用键路径和键生成器

const openReq = indexedDB.open('test', 7)
openReq.onupgradeneeded = () => {
  const db = openReq.result
  db.createObjectStore('users_mix2', { keyPath: 'id', autoIncrement: true })
}
openReq.onsuccess = () => {
  const db = openReq.result
  const tx = db.transaction(['users_mix2'], 'readwrite')
  tx.oncomplete = (e) => {
    console.log('tx oncomplete', e)
  }
  tx.onerror = (e) => {
    console.log('tx onerror', e)
  }
  const store = tx.objectStore('users_mix2')
  const req = store.add({ id: '123', name: '小王' }) // Key: "123"; Value: {id: "123", name: "小王"}
  req.onsuccess = () => {
    console.log('store onsuccess', req.result)
  }
  req.onerror = (e) => {
    console.log('store onerror', e)
  }
  store.add({ name: '小王' }) // Key: 1; Value: {id: 1, name: "小王"}
  store.add({ name: '小王' }) // Key: 2; Value: {id: 2, name: "小王"}
}

新建仓库的同时添加数据

const openReq = indexedDB.open('test', 8)
openReq.onupgradeneeded = () => {
  const db = openReq.result
  const store = db.createObjectStore('users_direct', { autoIncrement: true })
  const tx = store.transaction
  tx.oncomplete = () => {
    const tx2 = db.transaction(['users_direct'], 'readwrite')
    tx2.oncomplete = (e) => {
      console.log('tx oncomplete', e)
    }
    tx2.onerror = (e) => {
      console.log('tx onerror', e)
    }
    const store2 = tx2.objectStore('users_direct')
    const req = store2.add('小王') // Key: 1; Value: "小王"
    req.onsuccess = () => {
      console.log('store onsuccess', req.result)
    }
    req.onerror = (e) => {
      console.log('store onerror', e)
    }
    store2.add('小王') // Key: 2; Value: "小王"
    store2.add('小张') // Key: 3; Value: "小张"
  }
}
openReq.onsuccess = () => {
  const db = openReq.result
  const tx = db.transaction(['users_direct'])
  tx.oncomplete = (e) => {
    console.log('tx oncomplete', e)
  }
  tx.onerror = (e) => {
    console.log('tx onerror', e)
  }
  const store = tx.objectStore('users_direct')
  const req = store.count() // 3
  req.onsuccess = () => {
    console.log('req success count', req.result)
  }
  req.onerror = (e) => {
    console.log('req error', e)
  }
}

清空对象仓库

IDBObjectStore.clear(): IDBRequest<undefined>
const openReq = indexedDB.open('test', 9)
openReq.onupgradeneeded = () => {
  const db = openReq.result
  const store = db.createObjectStore('users_clear')
  const tx = store.transaction
  tx.oncomplete = () => {
    const tx2 = db.transaction(['users_clear'], 'readwrite')
    tx2.oncomplete = (e) => {
      console.log('tx oncomplete', e)
    }
    tx2.onerror = (e) => {
      console.log('tx onerror', e)
    }
    const store2 = tx2.objectStore('users_clear')
    const req = store2.add('小王', 'x') // Key: "x"; Value: "小王"
    req.onsuccess = () => {
      console.log('store onsuccess', req.result)
    }
    req.onerror = (e) => {
      console.log('store onerror', e)
    }
  }
}
openReq.onsuccess = () => {
  const db = openReq.result
  const tx = db.transaction('users_clear', 'readwrite')
  tx.oncomplete = (e) => {
    console.log('tx oncomplete', e)
  }
  tx.onerror = (e) => {
    console.log('tx onerror', e)
  }
  const store = tx.objectStore('users_clear')
  const req = store.clear()
  req.onsuccess = (e) => {
    console.log('clear success', e)
  }
  req.onerror = () => {
    console.log('clear error', e)
  }
}

删除对象仓库

IDBDatabase.deleteObjectStore(name: string): void
const openReq = indexedDB.open('test', 10)
openReq.onupgradeneeded = () => {
  const db = openReq.result
  db.deleteObjectStore('users_clear')
}
openReq.onsuccess = () => {
  console.log('delete success')
}

对象仓库是否已经存在

IDBDatabase.objectStoreNames.contains(string: string): boolean
const openReq = indexedDB.open('test', 11)
openReq.onupgradeneeded = () => {
  console.log('open onupgradeneeded')
  const db = openReq.result
  console.log('objectStoreNames', db.objectStoreNames)
  console.log('objectStoreNames contains users_gen', db.objectStoreNames.contains('users_gen'))
}
openReq.onsuccess = () => {
  console.log('open onsuccess')
  const db = openReq.result
  console.log('objectStoreNames', db.objectStoreNames)
  console.log('objectStoreNames contains users_arr', db.objectStoreNames.contains('users_arr'))
}

索引

新建索引

IDBObjectStore.createIndex(name: string, keyPath: string | string[], options?: IDBIndexParameters): IDBIndex
const openReq = indexedDB.open('test', 12)
openReq.onupgradeneeded = () => {
  const db = openReq.result
  const store = db.createObjectStore('user_idx', { keyPath: 'id' })
  const index = store.createIndex('name', 'name', { unique: false })
  console.log(index.name, index.keyPath, index.multiEntry, index.unique, index.objectStore)
  store.createIndex('age', 'age', { unique: false, multiEntry: true })
  store.createIndex('name_age', ['name', 'age'], { unique: true, multiEntry: false })
}
openReq.onsuccess = () => {
  const db = openReq.result
  const tx = db.transaction('user_idx', 'readwrite')
  tx.oncomplete = (e) => {
    console.log('tx oncomplete', e)
  }
  tx.onerror = (e) => {
    console.log('tx onerror', e)
  }
  const store = tx.objectStore('user_idx')
  const req = store.add({ id: '123', name: '小王', age: 20 })
  req.onsuccess = () => {
    console.log('store onsuccess', req.result)
  }
  req.onerror = (e) => {
    console.log('store onerror', e)
  }
  store.add({ id: '234', name: '小王', age: 22 })
  /**
   * Key: "123"; Value: {id: "123", name: "小王", age: 20}
   * Key: "234"; Value: {id: "234", name: "小王", age: 22}
   * 
   * Index - name:
   *  Key: "小王"; Primary Key: "123"; Value: {id: "123", name: "小王", age: 20}
   *  Key: "小王"; Primary Key: "234"; Value: {id: "234", name: "小王", age: 22}
   * 
   * Index - age:
   *  Key: 20; Primary Key: "123"; Value: {id: "123", name: "小王", age: 20}
   *  Key: 22; Primary Key: "234"; Value: {id: "234", name: "小王", age: 22}
   * 
   * Index - name_age:
   *  Key: ["小王", 20]; Primary Key: "123"; Value: {id: "123", name: "小王", age: 20}
   *  Key: ["小王", 22]; Primary Key: "234"; Value: {id: "234", name: "小王", age: 22}
   */
}

删除索引

IDBObjectStore.deleteIndex(name: string): void
const openReq = indexedDB.open('test', 13)
openReq.onupgradeneeded = () => {
  console.log('openReq', openReq.transaction)
  const tx = openReq.transaction // 升级事务(允许执行任何操作,包括删除和创建对象存储和索引的操作)
  tx.oncomplete = (e) => {
    console.log('tx oncomplete', e)
  }
  tx.onerror = (e) => {
    console.log('tx onerror', e)
  }
  const store = tx.objectStore('user_idx')
  store.deleteIndex('age')
}
openReq.onsuccess = () => {
  const db = openReq.result
  const tx = db.transaction('user_idx', 'readwrite')
  tx.oncomplete = (e) => {
    console.log('tx oncomplete', e)
  }
  tx.onerror = (e) => {
    console.log('tx onerror', e)
  }
  const store = tx.objectStore('user_idx')
  const req = store.add({ id: '345', name: '小王', age: 23 })
  req.onsuccess = () => {
    console.log('store onsuccess', req.result)
  }
  req.onerror = (e) => {
    console.log('store onerror', e)
  }
  /**
   * Key: "123"; Value: {id: "123", name: "小王", age: 20}
   * Key: "234"; Value: {id: "234", name: "小王", age: 22}
   * Key: "345"; Value: {id: "345", name: "小王", age: 23}
   * 
   * Index - name:
   *  Key: "小王"; Primary Key: "123"; Value: {id: "123", name: "小王", age: 20}
   *  Key: "小王"; Primary Key: "234"; Value: {id: "234", name: "小王", age: 22}
   *  Key: "小王"; Primary Key: "345"; Value: {id: "345", name: "小王", age: 23}
   * 
   * Index - name_age:
   *  Key: ["小王", 20]; Primary Key: "123"; Value: {id: "123", name: "小王", age: 20}
   *  Key: ["小王", 22]; Primary Key: "234"; Value: {id: "234", name: "小王", age: 22}
   *  Key: ["小王", 23]; Primary Key: "345"; Value: {id: "345", name: "小王", age: 23}
   */
}

更新索引

IDBOpenDBRequest.transaction.objectStore(name: string): IDBObjectStore
const openReq = indexedDB.open('test', 14)
openReq.onupgradeneeded = () => {
  const tx = openReq.transaction // 升级事务
  tx.oncomplete = (e) => {
    console.log('tx1 oncomplete', e)
  }
  tx.onerror = (e) => {
    console.log('tx1 onerror', e)
  }
  const store = tx.objectStore('user_idx')
  store.deleteIndex('name')
  store.deleteIndex('name_age') // unique: true -> false
  store.createIndex('name_age', ['name', 'age'], { unique: false, multiEntry: false })
}
openReq.onsuccess = () => {
  const db = openReq.result
  const tx = db.transaction('user_idx', 'readwrite')
  tx.oncomplete = (e) => {
    console.log('tx2 oncomplete', e)
  }
  tx.onerror = (e) => {
    console.log('tx2 onerror', e)
  }
  const store = tx.objectStore('user_idx')
  const req = store.add({ id: '456', name: '小王', age: 23 })
  req.onsuccess = (e) => {
    console.log('store onsuccess', e)
  }
  req.onerror = (e) => {
    console.log('store onerror', e)
  }
  /**
   * Key: "123"; Value: {id: "123", name: "小王", age: 20}
   * Key: "234"; Value: {id: "234", name: "小王", age: 22}
   * Key: "345"; Value: {id: "345", name: "小王", age: 23}
   * Key: "456"; Value: {id: "456", name: "小王", age: 23}
   * 
   * Index - name_age:
   *  Key: ["小王", 20]; Primary Key: "123"; Value: {id: "123", name: "小王", age: 20}
   *  Key: ["小王", 22]; Primary Key: "234"; Value: {id: "234", name: "小王", age: 22}
   *  Key: ["小王", 23]; Primary Key: "345"; Value: {id: "345", name: "小王", age: 23}
   *  Key: ["小王", 23]; Primary Key: "456"; Value: {id: "456", name: "小王", age: 23}
   */
}

数据操作

新建仓库和测试数据

IDBObjectStore.add(value: any, key?: IDBValidKey): IDBRequest<IDBValidKey>
const openReq = indexedDB.open('test', 15)
openReq.onupgradeneeded = () => {
  const db = openReq.result
  // userInfo 至少包含 name, phone, addr 三个属性(id 属性没有可以自动生成)
  const store = db.createObjectStore('user_info', { keyPath: 'id', autoIncrement: true })
  store.createIndex('name', 'name', { unique: false })
  store.createIndex('phone', 'phone', { unique: true })
  store.createIndex('name_addr', ['name', 'addr'], { unique: true, multiEntry: false })
}
openReq.onsuccess = () => {
  const db = openReq.result
  const tx = db.transaction('user_info', 'readwrite')
  tx.oncomplete = () => { /*...*/ }
  const store = tx.objectStore('user_info')
  const userInfos = [
    { name: '小王', phone: '13122223333', addr: 'A', id: '1234' },
    { name: '小王', phone: '13122224444', addr: 'B', id: '5678' },
    { name: '小张', phone: '13122225555', addr: 'A' },
    { name: '小吴', phone: '13122226666', addr: 'C' }
  ]
  userInfos.forEach(user => {
    const req = store.add(user)
    req.onsuccess = () => {
      console.log('add result', req.result)
    }
  })
}

使用主键检索

查询记录数

IDBObjectStore.count(key?: IDBValidKey | IDBKeyRange): IDBRequest<number>
const openReq = indexedDB.open('test', 15)
openReq.onsuccess = () => {
  const db = openReq.result
  const tx = db.transaction('user_info', 'readonly')
  tx.oncomplete = () => { /*...*/ }
  const store = tx.objectStore('user_info')
  const req = store.count()
  req.onsuccess = () => {
    console.log('get count', req.result) // 4
  }
}

查询数据

IDBObjectStore.get(query: IDBValidKey | IDBKeyRange): IDBRequest<any | undefined>
const openReq = indexedDB.open('test', 15)
openReq.onsuccess = () => {
  const db = openReq.result
  const tx = db.transaction('user_info', 'readonly')
  tx.oncomplete = () => { /*...*/ }
  const store = tx.objectStore('user_info')
  const req = store.get(2)
  req.onsuccess = () => {
    console.log('get result', req.result) // {name: "小吴", phone: "13122226666", addr: "C", id: 2}
  }
}
  • 使用不存在的主键查询
const openReq = indexedDB.open('test', 15)
openReq.onsuccess = () => {
  const db = openReq.result
  const tx = db.transaction('user_info', 'readonly')
  tx.oncomplete = () => { /*...*/ }
  const store = tx.objectStore('user_info')
  const req = store.get(3)
  req.onsuccess = () => {
    console.log('get result', req.result) // undefined
  }
}

查询所有数据

IDBObjectStore.getAll(query?: IDBValidKey | IDBKeyRange | null, count?: number): IDBRequest<any[]>
const openReq = indexedDB.open('test', 15)
openReq.onsuccess = () => {
  const db = openReq.result
  const tx = db.transaction('user_info', 'readonly')
  tx.oncomplete = () => { /*...*/ }
  const store = tx.objectStore('user_info')
  const req = store.getAll()
  req.onsuccess = () => {
    console.log('get result', req.result)
    // [
    //   { name: "小张", phone: "13122225555", addr: "A", id: 1 },
    //   { name: "小吴", phone: "13122226666", addr: "C", id: 2 },
    //   { name: "小王", phone: "13122223333", addr: "A", id: "1234" },
    //   { name: "小王", phone: "13122224444", addr: "B", id: "5678" }
    // ]
  }
}
  • 查询指定范围内的所有数据
const openReq = indexedDB.open('test', 15)
openReq.onsuccess = () => {
  const db = openReq.result
  const tx = db.transaction('user_info', 'readonly')
  tx.oncomplete = () => { /*...*/ }
  const store = tx.objectStore('user_info')
  const keyRange = IDBKeyRange.bound(1, 2)
  const req = store.getAll(keyRange)
  req.onsuccess = () => {
    console.log('get result', req.result)
    // [
    //   { name: "小张", phone: "13122225555", addr: "A", id: 1 },
    //   { name: "小吴", phone: "13122226666", addr: "C", id: 2 }
    // ]
  }
}

查询主键

IDBObjectStore.getKey(query: IDBValidKey | IDBKeyRange): IDBRequest<IDBValidKey | undefined>
const openReq = indexedDB.open('test', 15)
openReq.onsuccess = () => {
  const db = openReq.result
  const tx = db.transaction('user_info', 'readonly')
  tx.oncomplete = () => { /*...*/ }
  const store = tx.objectStore('user_info')
  const req = store.getKey(2)
  req.onsuccess = () => {
    console.log('get result', req.result) // 2
  }
}

查询所有主键

IDBObjectStore.getAllKeys(query?: IDBValidKey | IDBKeyRange | null, count?: number): IDBRequest<IDBValidKey[]>
const openReq = indexedDB.open('test', 15)
openReq.onsuccess = () => {
  const db = openReq.result
  const tx = db.transaction('user_info', 'readonly')
  tx.oncomplete = () => { /*...*/ }
  const store = tx.objectStore('user_info')
  const req = store.getAllKeys()
  req.onsuccess = () => {
    console.log('get result', req.result) //  [1, 2, "1234", "5678"]
  }
}
  • 查询指定范围内的所有键
const openReq = indexedDB.open('test', 15)
openReq.onsuccess = () => {
  const db = openReq.result
  const tx = db.transaction('user_info', 'readonly')
  tx.oncomplete = () => { /*...*/ }
  const store = tx.objectStore('user_info')
  const keyRange = IDBKeyRange.bound(1, 2)
  const req = store.getAllKeys(keyRange)
  req.onsuccess = () => {
    console.log('get result', req.result) // [1, 2]
  }
}

更新数据

IDBObjectStore.put(value: any, key?: IDBValidKey): IDBRequest<IDBValidKey>
const openReq = indexedDB.open('test', 15)
openReq.onsuccess = () => {
  const db = openReq.result
  const tx = db.transaction('user_info', 'readwrite')
  tx.oncomplete = () => { /*...*/ }
  const store = tx.objectStore('user_info')
  const getReq = store.get(2)
  getReq.onsuccess = () => {
    const data = getReq.result
    console.log('get result', data) // {name: "小吴", phone: "13122226666", addr: "C", id: 2}
    data.phone = '13122227777'
    data.addr = 'D'
    const putReq = store.put(data) // 更新数据
    putReq.onsuccess = () => {
      console.log('put result', putReq.result) // 2
    }
    putReq.onerror = (e) => {
      console.log('put error', e)
    }
  }
  const putReq = store.put({ name: "小王", phone: "13122228888", addr: "D" }) // 新增数据
  putReq.onsuccess = () => {
    console.log('put result', putReq.result) // 3
  }
  putReq.onerror = (e) => {
    console.log('put error', e)
  }
}

删除数据

IDBObjectStore.delete(key: IDBValidKey | IDBKeyRange): IDBRequest<undefined>
const openReq = indexedDB.open('test', 15)
openReq.onsuccess = () => {
  const db = openReq.result
  const tx = db.transaction('user_info', 'readwrite')
  tx.oncomplete = () => { /*...*/ }
  const store = tx.objectStore('user_info')
  const req = store.delete(1)
  req.onsuccess = () => {
    console.log('del result', req.result) // undefined
  }
}

使用索引检索

IDBObjectStore.index(name: string): IDBIndex

查询记录数

IDBIndex.count(key?: IDBValidKey | IDBKeyRange): IDBRequest<number>
const openReq = indexedDB.open('test', 15)
openReq.onsuccess = () => {
  const db = openReq.result
  const tx = db.transaction('user_info', 'readonly')
  tx.oncomplete = () => { /*...*/ }
  const store = tx.objectStore('user_info')
  console.log(store.indexNames) // {0: "name", 1: "name_addr", 2: "phone", length: 3}
  const index = store.index('name')
  const req = index.count('小王')
  req.onsuccess = () => {
    console.log('get result', req.result) // 3
  }
}

查询数据

IDBIndex.get(key: IDBValidKey | IDBKeyRange): IDBRequest<any | undefined>
const openReq = indexedDB.open('test', 15)
openReq.onsuccess = () => {
  const db = openReq.result
  const tx = db.transaction('user_info', 'readonly')
  tx.oncomplete = () => { /*...*/ }
  const store = tx.objectStore('user_info')
  const index = store.index('name')
  const req = index.get('小王')
  req.onsuccess = () => {
    console.log('get result', req.result) // {name: "小王", phone: "13122228888", addr: "D", id: 3}
  }
}

查询所有数据

IDBIndex.getAll(query?: IDBValidKey | IDBKeyRange | null, count?: number): IDBRequest<any[]>
const openReq = indexedDB.open('test', 15)
openReq.onsuccess = () => {
  const db = openReq.result
  const tx = db.transaction('user_info', 'readonly')
  tx.oncomplete = () => { /*...*/ }
  const store = tx.objectStore('user_info')
  const index = store.index('name')
  const req = index.getAll('小王')
  req.onsuccess = () => {
    console.log('get result', req.result)
    // [
    //   { name: "小王", phone: "13122228888", addr: "D", id: 3 },
    //   { name: "小王", phone: "13122223333", addr: "A", id: "1234" },
    //   { name: "小王", phone: "13122224444", addr: "B", id: "5678" }
    // ]
  }
}

查询索引键

IDBIndex.getKey(key: IDBValidKey | IDBKeyRange): IDBRequest<IDBValidKey | undefined>
const openReq = indexedDB.open('test', 15)
openReq.onsuccess = () => {
  const db = openReq.result
  const tx = db.transaction('user_info', 'readonly')
  tx.oncomplete = () => { /*...*/ }
  const store = tx.objectStore('user_info')
  const index = store.index('name')
  const req = index.getKey('小王')
  req.onsuccess = () => {
    console.log('get result', req.result) // 3
  }
}

查询所有索引键

IDBIndex.getAllKeys(query?: IDBValidKey | IDBKeyRange | null, count?: number): IDBRequest<IDBValidKey[]>
const openReq = indexedDB.open('test', 15)
openReq.onsuccess = () => {
  const db = openReq.result
  const tx = db.transaction('user_info', 'readonly')
  tx.oncomplete = () => { /*...*/ }
  const store = tx.objectStore('user_info')
  const index = store.index('name')
  const req = index.getAllKeys('小王')
  req.onsuccess = () => {
    console.log('get result', req.result) // [3, "1234", "5678"]
  }
}

使用游标检索

查询索引数据

IDBIndex.openCursor(query?: IDBValidKey | IDBKeyRange | null, direction?: IDBCursorDirection): IDBRequest<IDBCursorWithValue | null>
IDBCursorWithValue.value: any
  const openReq = indexedDB.open('test', 15)
  openReq.onsuccess = () => {
    const db = openReq.result
    const tx = db.transaction('user_info', 'readonly')
    tx.oncomplete = () => { /*...*/ }
    const store = tx.objectStore('user_info')
    const index = store.index('name')
    const req = index.openCursor('小王')
    req.onsuccess = () => {
      const cursor = req.result
      if (cursor) {
        console.log('get', cursor.value, cursor.key, cursor.primaryKey)
      }
    }
    // {name: "小王", phone: "13122228888", addr: "D", id: 3} 小王 3
  }
  • 查询所有索引数据
const openReq = indexedDB.open('test', 15)
openReq.onsuccess = () => {
  const db = openReq.result
  const tx = db.transaction('user_info', 'readonly')
  tx.oncomplete = () => { /*...*/ }
  const store = tx.objectStore('user_info')
  const index = store.index('name')
  const req = index.openCursor('小王')
  req.onsuccess = () => {
    const cursor = req.result
    if (cursor) {
      console.log('get', cursor.value, cursor.key, cursor.primaryKey)
      cursor.continue()
    }
  }
  // {name: "小王", phone: "13122228888", addr: "D", id: 3} 小王 3
  // {name: "小王", phone: "13122223333", addr: "A", id: "1234"} 小王 1234
  // {name: "小王", phone: "13122224444", addr: "B", id: "5678"} 小王 5678
}

更新索引数据

IDBCursor.update(value: any): IDBRequest<IDBValidKey>
const openReq = indexedDB.open('test', 15)
openReq.onsuccess = () => {
  const db = openReq.result
  const tx = db.transaction('user_info', 'readwrite')
  tx.oncomplete = () => { /*...*/ }
  const store = tx.objectStore('user_info')
  const index = store.index('name')
  const req = index.openCursor('小王')
  req.onsuccess = () => {
    const cursor = req.result
    if (cursor) {
      const data = cursor.value
      // data.addr = `${data.addr}${Math.random() * 100 | 0}`
      data.age = 25
      const updataReq = cursor.update(data)
      updataReq.onsuccess = () => {
        console.log('update result', updataReq.result)
      }
      updataReq.onerror = () => { /*...*/ }
      cursor.continue()
    }
  }
  // 3 1234 5678
}

删除索引数据

IDBCursor.delete(): IDBRequest<undefined>
const openReq = indexedDB.open('test', 15)
openReq.onsuccess = () => {
  const db = openReq.result
  const tx = db.transaction('user_info', 'readwrite')
  tx.oncomplete = () => { /*...*/ }
  const store = tx.objectStore('user_info')
  const index = store.index('name')
  const req = index.openCursor('小王')
  req.onsuccess = () => {
    const cursor = req.result
    if (cursor) {
      const data = cursor.value
      if (data.addr.includes('A')) {
        const delReq = cursor.delete()
        delReq.onsuccess = () => {
          console.log('del result', delReq.result)
        }
        delReq.onerror = (e) => { /*...*/ }
      }
      cursor.continue()
    }
  }
}

查询主键数据

IDBObjectStore.openCursor(query?: IDBValidKey | IDBKeyRange | null, direction?: IDBCursorDirection): IDBRequest<IDBCursorWithValue | null>
const openReq = indexedDB.open('test', 15)
openReq.onsuccess = () => {
  const db = openReq.result
  const tx = db.transaction('user_info', 'readonly')
  tx.oncomplete = () => { /*...*/ }
  const store = tx.objectStore('user_info')
  const req = store.openCursor()
  req.onsuccess = () => {
    const cursor = req.result
    if (cursor) {
      console.log('get', cursor.value, cursor.key, cursor.primaryKey)
      cursor.continue()
    }
  }
  // {name: "小吴", phone: "13122227777", addr: "D", id: 2} 2 2
  // {name: "小王", phone: "13122228888", addr: "D", id: 3, age: 25} 3 3
  // {name: "小王", phone: "13122224444", addr: "B", id: "5678", age: 25} 5678 5678
}

遍历数据

  • 设置步长
IDBCursor.advance(count: number): void
const openReq = indexedDB.open('test', 15)
openReq.onsuccess = () => {
  const db = openReq.result
  const tx = db.transaction('user_info', 'readonly')
  tx.oncomplete = () => { /*...*/ }
  const store = tx.objectStore('user_info')
  const req = store.openCursor()
  req.onsuccess = () => {
    const cursor = req.result
    if (cursor) {
      console.log('get', cursor.value)
      cursor.advance(2)
    }
  }
  // {name: "小吴", phone: "13122227777", addr: "D", id: 2}
  // {name: "小王", phone: "13122224444", addr: "B", id: "5678", age: 25}
}
  • 设置目标
IDBCursor.continue(key?: IDBValidKey): void
const openReq = indexedDB.open('test', 15)
openReq.onsuccess = () => {
  const db = openReq.result
  const tx = db.transaction('user_info', 'readonly')
  tx.oncomplete = () => { /*...*/ }
  const store = tx.objectStore('user_info')
  const req = store.openCursor()
  req.onsuccess = () => {
    const cursor = req.result
    if (cursor) {
      console.log('get', cursor.value)
      if (cursor.primaryKey !== '5678') {
        cursor.continue('5678')
      }
    }
  }
  // {name: "小吴", phone: "13122227777", addr: "D", id: 2}
  // {name: "小王", phone: "13122224444", addr: "B", id: "5678", age: 25}
}

补充

升级版本

  • 方式:指定更高的版本号
  • 时机1:需要创建、删除对象仓库
  • 时机2:需要创建、删除索引

管理数据库版本号的类

不考虑兼容性,可以使用 indexedDB.databases().then(databases => console.log(databases))

/**
 * @class 这是一个记录数据库版本号的类
 */
class DataBases {
  private readonly open: Promise<IDBDatabase>
  private constructor() {
    this.open = this.openDB()
  }
  private openDB() {
    return new Promise<IDBDatabase>((resolve, reject) => {
      console.log('open db')
      const openReq = indexedDB.open('databases')
      openReq.onupgradeneeded = () => openReq.result.createObjectStore('databases')
      openReq.onsuccess = () => resolve(openReq.result)
      openReq.onerror = () => reject(openReq.error)
    })
  }
  private useStore(callback: (store: IDBObjectStore) => void, mode: IDBTransactionMode = 'readonly') {
    return this.open.then(db => {
      return new Promise<void>((resolve, reject) => {
        const tx = db.transaction('databases', mode)
        tx.oncomplete = () => resolve()
        tx.onabort = tx.onerror = (e) => reject(tx.error)
        callback(tx.objectStore('databases'))
      })
    })
  }

  get(key) {
    let version = 1
    return this.useStore(store => {
      store.get(key).onsuccess = (e) => {
        version = (e.target as IDBRequest).result || 1
      }
    }).then(() => version)
  }
  set(key, value) {
    return this.useStore(store => { store.put(value, key) }, 'readwrite').then(() => value)
  }
  del(key) {
    return this.useStore(store => { store.delete(key) }, 'readwrite')
  }
  key(index: number) {
    let name: string
    return this.useStore(store => {
      store.getAllKeys().onsuccess = (e) => {
        const keys = (e.target as IDBRequest).result
        name = index >= 0 && index <= keys.length - 1 ? keys[index] : null
      }
    }).then(() => name || null)
  }
  length() {
    let len = 0
    return this.useStore(store => {
      store.count().onsuccess = (e) => {
        len = (e.target as IDBRequest).result || 0
      }
    }).then(() => len)
  }

  private static _instance: DataBases
  static getInstance(): DataBases {
    if (!DataBases._instance) {
      DataBases._instance = new DataBases()
    }
    return DataBases._instance
  }
  static getVersion(dbName: string) {
    return DataBases.getInstance().get(dbName)
  }
  static setVersion(dbName: string, version: number) {
    return DataBases.getInstance().set(dbName, version)
  }
  static removeVersion(dbName: string) {
    return DataBases.getInstance().del(dbName)
  }
  static getKey(index: number) {
    return DataBases.getInstance().key(index)
  }
  static getLength() {
    return DataBases.getInstance().length()
  }
}

参考: idb-keyval

  • 使用
DataBases.getVersion('testdb').then(version => {
  console.log('get version', version) // get version 1
  return DataBases.setVersion('testdb', ++version)
}).then(newVersion => {
  console.log('set version', newVersion) // set version 2
  return DataBases.getLength()
}).then(len => {
  console.log('get length', len) // get length 1
  return DataBases.getKey(len - 1)
}).then(key => {
  console.log('get key', key) // get key testdb
  return DataBases.removeVersion('testdb')
}).then(() => {
  console.log('remove success') // remove success
})

参考