主题
IndexedDB
IndexedDB 是浏览器提供的本地结构化存储方案,适合存储较大体积、需要索引查询或离线持久化的数据。相比 localStorage,它不是简单的字符串键值存储,而是一个异步的对象数据库。
适合什么场景
- 离线缓存列表、草稿、表单数据
- 存储大量结构化数据
- 需要索引查询的数据
- 前端需要做本地同步或断点恢复
和 Web Storage 的区别
| 对比项 | Web Storage | IndexedDB |
|---|---|---|
| 数据模型 | 字符串键值对 | 对象数据库 |
| API 风格 | 同步 | 异步 |
| 数据量 | 较小 | 较大 |
| 查询能力 | 很弱 | 支持索引 |
| 适用场景 | 简单配置、小状态 | 离线数据、大量数据 |
核心概念
Database:数据库实例Object Store:对象仓库,类似表Transaction:事务,所有读写都在事务中进行Index:索引,用于按字段查询
可以先简单记成:
- 数据库存整体
- 仓库存一类对象
- 索引用来按字段查
- 事务负责控制一次读写过程
打开数据库
js
const request = indexedDB.open('vfan-docs', 1)
request.onerror = () => {
console.error('数据库打开失败')
}
request.onsuccess = () => {
const db = request.result
console.log('数据库打开成功', db)
}indexedDB.open(name, version) 不会同步直接返回数据库实例,而是先返回一个打开请求对象。
初始化仓库
当数据库第一次创建或版本升级时,会触发 onupgradeneeded。
js
const request = indexedDB.open('vfan-docs', 1)
request.onupgradeneeded = () => {
const db = request.result
if (!db.objectStoreNames.contains('articles')) {
const store = db.createObjectStore('articles', {
keyPath: 'id',
})
store.createIndex('title', 'title', { unique: false })
}
}这一阶段通常只做结构初始化或迁移,不负责日常读写。
写入数据
js
const request = indexedDB.open('vfan-docs', 1)
request.onsuccess = () => {
const db = request.result
const tx = db.transaction('articles', 'readwrite')
const store = tx.objectStore('articles')
store.put({
id: 1,
title: 'IndexedDB',
content: 'local database',
})
tx.oncomplete = () => {
console.log('写入完成')
}
}写入通常发生在 readwrite 事务里。
读取单条数据
js
const request = indexedDB.open('vfan-docs', 1)
request.onsuccess = () => {
const db = request.result
const tx = db.transaction('articles', 'readonly')
const store = tx.objectStore('articles')
const getRequest = store.get(1)
getRequest.onsuccess = () => {
console.log(getRequest.result)
}
}读取全部数据
js
const request = indexedDB.open('vfan-docs', 1)
request.onsuccess = () => {
const db = request.result
const tx = db.transaction('articles', 'readonly')
const store = tx.objectStore('articles')
const getAllRequest = store.getAll()
getAllRequest.onsuccess = () => {
console.log(getAllRequest.result)
}
}删除数据
js
const request = indexedDB.open('vfan-docs', 1)
request.onsuccess = () => {
const db = request.result
const tx = db.transaction('articles', 'readwrite')
const store = tx.objectStore('articles')
store.delete(1)
}事务的意义
IndexedDB 的读写都发生在事务中:
readonly:只读事务readwrite:读写事务
事务完成后会自动关闭,因此不要把事务打开后长时间挂着不继续操作,否则容易遇到“事务已失效”的问题。
版本升级
如果要新增仓库、修改索引或调整结构,需要提高版本号:
js
indexedDB.open('vfan-docs', 2)然后在 onupgradeneeded 里做迁移逻辑。
这也是 IndexedDB 的一个重要边界:
- 数据结构调整不是随时直接改
- 往往要通过版本升级统一迁移
使用建议
- 小数据优先
localStorage或sessionStorage,不要为简单状态引入过重方案。 - 需要离线缓存或大量结构化数据时,再考虑
IndexedDB。 - 原生 API 偏底层,实际项目里经常会配合
idb之类的轻量封装库使用。 - 设计数据结构时,先想清楚主键和索引,否则后续升级成本较高。
