前端开发这几年,localStorage 和 sessionStorage 用得最多,cookie 偶尔也要打打交道。但说到 IndexedDB,很多人的反应是:"听说过,但没用过。"
今天聊聊这个被低估的浏览器内置数据库。
一、为什么需要另一个存储方案?
先看个实际场景。朋友公司做电商后台,产品经理要求:"能不能在列表页缓存 5000 条商品数据,让筛选和搜索快一点?"
第一版用 localStorage:
javascript
// 存储
localStorage.setItem('products', JSON.stringify(products)) // 5000条数据,页面卡了2秒
// 搜索
const keyword = '手机'
const allProducts = JSON.parse(localStorage.getItem('products')) // 又卡1秒
const results = allProducts.filter(p => p.name.includes(keyword)) // 遍历5000次
上线后用户反馈:"筛选时浏览器像卡住了一样。"
问题在哪?localStorage 有硬伤:
- 同步操作,数据量大就阻塞页面
- 只能存字符串,对象要序列化
- 容量小(通常 5-10MB)
- 只能全量读取,无法高效查询
二、IndexedDB 是什么?
简单说,它是浏览器里的 NoSQL 数据库。2011 年就出现了,但很多人不知道或觉得"用不上"。
几个关键特点:
- 容量大:通常能占硬盘 50%,几个 GB 没问题
- 异步操作:不卡页面
- 支持索引:查询速度快
- 能存多种类型:对象、文件、二进制数据都行
三、一个简单示例
如果你没用过,先看看基本用法:
javascript
// 1. 打开数据库
const request = indexedDB.open('myDB', 1)
// 2. 创建表结构(第一次或升级时)
request.onupgradeneeded = function(event) {
const db = event.target.result
// 创建对象存储(类似表)
const store = db.createObjectStore('products', {
keyPath: 'id', // 主键
autoIncrement: true // 自动生成ID
})
// 创建索引(加速查询的关键)
store.createIndex('name', 'name') // 按名称查
store.createIndex('price', 'price') // 按价格查
store.createIndex('category', 'category') // 按分类查
}
// 3. 数据库就绪
request.onsuccess = function(event) {
const db = event.target.result
console.log('数据库已就绪')
}
四、核心优势:查询性能
这是 IndexedDB 真正厉害的地方。同样的 5000 条商品数据,查询完全不同:
javascript
// 用索引查,不需要遍历所有数据
async function searchProducts(keyword) {
const transaction = db.transaction(['products'], 'readonly')
const store = transaction.objectStore('products')
const index = store.index('name') // 使用索引
// 只搜索相关范围
const range = IDBKeyRange.bound(keyword, keyword + '\uffff')
const request = index.openCursor(range)
return new Promise((resolve) => {
const results = []
request.onsuccess = function(event) {
const cursor = event.target.result
if (cursor) {
results.push(cursor.value)
cursor.continue() // 继续下一个
} else {
resolve(results) // 搜索完成
}
}
})
}
// 毫秒级响应,不卡页面
const results = await searchProducts('手机')
你可以创建多个索引,实现各种复杂查询:
- 价格区间筛选
- 多条件组合查询
- 分类统计
- 模糊搜索
五、适用场景
什么情况下该考虑 IndexedDB?
1. 离线应用
邮件客户端、文档编辑器、笔记应用。数据先存本地,有网再同步。
2. 大数据缓存
电商商品目录、大量配置项、历史数据。替代接口频繁请求。
3. 文件管理
图片、PDF、音视频的本地缓存。不用每次都下载。
4. 游戏数据
存档、配置、资源文件。支持离线游戏。
5. 分析数据
收集用户行为,批量上传。避免频繁网络请求。
六、实用建议
1. 用封装库简化开发
原生 API 确实有点繁琐。推荐这些库:
javascript
// 用 idb 库(推荐)
import { openDB } from 'idb'
const db = await openDB('my-db', 1, {
upgrade(db) {
db.createObjectStore('products')
}
})
// 操作简单多了
await db.add('products', { name: '商品1', price: 100 })
const products = await db.getAll('products')
2. 渐进增强
先判断支持性,不支持就降级:
javascript
function getStorage() {
if ('indexedDB' in window) {
return {
type: 'indexedDB',
save: saveToIndexedDB,
load: loadFromIndexedDB
}
} else {
console.log('降级到 localStorage')
return {
type: 'localStorage',
save: saveToLocalStorage,
load: loadFromLocalStorage
}
}
}
3. 注意版本迁移
修改表结构需要升级版本:
javascript
const request = indexedDB.open('myDB', 2) // 版本号+1
request.onupgradeneeded = function(event) {
const db = event.target.result
const oldVersion = event.oldVersion
if (oldVersion < 1) {
// 初始版本逻辑
}
if (oldVersion < 2) {
// 版本2的升级逻辑
// 比如添加新索引
const store = event.currentTarget.transaction.objectStore('products')
store.createIndex('createdAt', 'createdAt')
}
}
七、什么时候不用?
IndexedDB 虽好,但也不是万能:
- 存个用户 token → 用 localStorage 或 cookie
- 会话级临时数据 → 用 sessionStorage
- 简单配置项 → localStorage 更方便
- 需要服务端读取 → cookie
记住:技术选型要看具体需求,不是越高级越好。
八、开始尝试
如果你从没用过 IndexedDB,可以从这些开始:
- 缓存接口数据:把频繁请求的 API 结果缓存起来
- 离线收藏功能:用户收藏的内容存本地
- 图片懒加载缓存:看过的图片存起来
- 表单草稿:复杂的表单数据实时保存
不需要一开始就大动干戈。找个合适的场景,先试试水。
写在最后
IndexedDB 在前端领域存在感不强,可能因为它解决的问题不是每个项目都会遇到。但当你真的需要处理大量客户端数据时,它会是个很好的选择。
技术没有绝对的好坏,只有合适与否。知道它的存在,了解它的能力,当合适的需求出现时,你就能做出更好的选择。
看完有点兴趣了?可以在个人项目里试试 IndexedDB,遇到问题欢迎交流。如果你已经在用,有什么经验或踩坑故事?评论区聊聊。