localStorage sessionStorage IndexedDB 存储方案选型
FreeGuideOnline
最新
2026-07-05
javascript // 存储数据(仅支持字符串) localStorage.setItem('theme', 'dark'); localStorage.setItem('user', JSON.stringify({ name: 'Alice', age: 30 }));
// 读取数据 const theme = localStorage.getItem('theme'); // 'dark' const user = JSON.parse(localStorage.getItem('user'));
// 删除单个键 localStorage.removeItem('theme');
// 清空所有 localStorage.clear();
// 遍历 for (let i = 0; i < localStorage.length; i++) { const key = localStorage.key(i); console.log(key, localStorage.getItem(key)); }
### 1.3 常见陷阱与最佳实践
- **存放对象时必须序列化**:`localStorage` 只接受字符串,存入对象或数组前使用 `JSON.stringify()`,取出时使用 `JSON.parse()`。
- **不要把敏感信息暴露在前端**:Token、密码等不应直接放入 `localStorage`,易受 XSS 攻击。
- **配额超限异常处理**:
```javascript
try {
localStorage.setItem('largeData', hugeString);
} catch (e) {
if (e.name === 'QuotaExceededError') {
console.error('本地存储空间已满');
// 可考虑清理部分数据或降级处理
}
}
- 适合存储:用户偏好设置(语言、主题)、不敏感的认证令牌(需权衡安全性)、表单草稿(若不需跨标签页)。
2. sessionStorage:仅存于会话的临时仓库
2.1 基本特性
- 生命周期:数据仅在当前浏览器标签页的会话期间存在。关闭标签页或浏览器窗口时数据被清除。刷新或前进后退页面仍可保留。
- 隔离性:同一页面在新标签页打开时,会获得全新的、空的
sessionStorage,即使 URL 相同。 - 容量与 API:与
localStorage基本一致,同为同步字符串存储,约 5MB 限制。
2.2 基础用法
// 存储
sessionStorage.setItem('step', '2');
sessionStorage.setItem('formDraft', JSON.stringify({ name: 'Bob', email: '' }));
// 读取
const currentStep = sessionStorage.getItem('step');
// 移除
sessionStorage.removeItem('step');
sessionStorage.clear();
2.3 经典使用场景
- 多步骤表单:保存用户输入的各步骤数据,避免因意外跳转或刷新丢失进度。
- 页面状态记忆:例如标签页切换、分页当前位置、滚动条位置(通过
scroll事件更新sessionStorage)。 - 一次性安全要求:不希望数据跨标签页共享,如交易确认页面临的 CSRF token 临时存放。
- 注意:不可在多个窗口间共享数据,如果需求是“多个标签页的购物车联动”,应放弃
sessionStorage。
3. IndexedDB:浏览器里的结构化数据库
3.1 基本特性
- 异步 API:所有操作均基于事务(Transaction)和事件/回调(或使用 Promise),不会阻塞主线程。
- 大容量:通常可达数百 MB 甚至更大,受浏览器磁盘配额限制(动态分配,部分浏览器达到可用空间的 60%)。
- 结构化存储:支持 JavaScript 原生类型,如对象、数组、二进制数据(Blob、File、ArrayBuffer),无需手动序列化。
- 索引与查询:通过创建索引,可按字段高效检索,支持按范围、游标、分页等操作。
- 事务安全:遵循事务模型,保证读一致性,写入失败可自动回滚。
- 同源限制:与
localStorage相同。
3.2 核心概念
- 数据库(Database):每个源可创建多个数据库,需指定名称和版本。
- 对象存储(Object Store):类似于关系型数据库的表。存放具体的记录。
- 索引(Index):为对象存储的某个属性建立的快速查找结构。
- 事务(Transaction):所有读写操作必须处在事务中,事务限定了作用域和模式(
readonly或readwrite)。
3.3 基本用法流程(使用原生 API)
// 打开数据库(若不存在则创建)
const request = indexedDB.open('MyApp', 1);
request.onupgradeneeded = (event) => {
const db = event.target.result;
// 创建对象存储,主键为 'id',自增
const store = db.createObjectStore('articles', { keyPath: 'id', autoIncrement: true });
// 在 title 字段上建索引
store.createIndex('titleIndex', 'title', { unique: false });
};
request.onsuccess = (event) => {
const db = event.target.result;
// 开始一个读写事务
const tx = db.transaction('articles', 'readwrite');
const store = tx.objectStore('articles');
// 添加数据
store.add({ title: '前端存储', content: '...', created: Date.now() });
tx.oncomplete = () => console.log('事务完成');
tx.onerror = (e) => console.error('事务失败', e);
};
request.onerror = (e) => console.error('数据库打开失败', e);
使用游标获取所有记录
const tx = db.transaction('articles', 'readonly');
const store = tx.objectStore('articles');
const allRecords = [];
store.openCursor().onsuccess = (e) => {
const cursor = e.target.result;
if (cursor) {
allRecords.push(cursor.value);
cursor.continue();
} else {
console.log('所有记录:', allRecords);
}
};
3.4 为何推荐使用类库
原生 API 基于事件且较为冗长。社区提供了优雅的 Promise 封装库:
- idb (Jake Archibald):轻量、完全基于 Promise,将原生 API 包装成现代风格。
import { openDB } from 'idb'; const db = await openDB('MyApp', 1, { upgrade(db) { const store = db.createObjectStore('articles', { keyPath: 'id', autoIncrement: true }); store.createIndex('titleIndex', 'title'); }, }); // 使用 await db.add('articles', { title: 'Hello', content: 'World' }); const all = await db.getAll('articles'); - Dexie.js:体积稍大但提供更高级的查询语法、表定义和调试工具,适合复杂应用。
3.5 适用场景
- 离线 Web 应用:缓存大量 API 响应、用户生成内容,实现完全离线使用。
- 复杂数据查询:需要按多个字段或日期范围检索信息,如邮件客户端、任务管理。
- 文件/二进制数据存储:可存储图片、视频等 Blob 对象。
- 大型数据集:当数据量远超 5MB(如地图瓦片、文档历史编辑记录)时,几乎是唯一选择。
4. 方案横向对比与选型决策
4.1 关键维度对比
| 维度 | localStorage | sessionStorage | IndexedDB |
|---|---|---|---|
| 容量 | ~5MB | ~5MB | 数百 MB ~ 设备可用空间 |
| 数据类型 | 字符串(需序列化) | 字符串 | 结构化对象、二进制 |
| API 模式 | 同步 | 同步 | 异步(事务) |
| 生命周期 | 永久(手动删除) | 标签页关闭即清除 | 永久 |
| 同源共享 | 同一源所有窗口 | 仅当前标签页 | 同一源所有窗口 |
| 查询能力 | 仅键值遍历 | 仅键值遍历 | 通过索引高效查询、游标 |
| 事务支持 | 无 | 无 | 完整事务 |
| 安全性 | 低(易受 XSS) | 低 | 低(需自行防护) |
| 复杂度 | 极低 | 极低 | 中高 |
4.2 选型决策速查表
| 你的需求 | 推荐方案 | 原因 |
|---|---|---|
| 存储少量用户设置(主题、语言) | localStorage |
简单永久存储,无容量问题。 |
| 临时保存表单输入,切换页面不丢失 | sessionStorage |
标签页内有效,关闭自动清除,避免数据泄露。 |
| 单页面内多个步骤的数据暂存 | sessionStorage |
步骤数据无需跨标签页,刷新仍能恢复。 |
| 离线浏览大量文章/列表 | IndexedDB |
支持索引、分页、大容量,可存储完整内容。 |
| 缓存 API 响应以加速下次访问 | IndexedDB (配合 Service Worker) |
可存储结构化和二进制数据,容量充足。 |
| 保存用户生成的图片/文件 | IndexedDB |
原生支持 Blob,大文件无压力。 |
| 只在当前标签页的一次性安全凭证 | sessionStorage |
关闭即毁,减少泄露面。 |
| 简单的跨标签页通信(如“退出登录”通知) | localStorage + storage 事件 |
遵循同源,A 页设置,B 页监听变化。 |
5. 综合应用示例:智能存储降级策略
实际项目中,可根据需要组合使用,并设计降级机制:
// 工具函数:优先使用 IndexedDB,若不可用降级到 localStorage
async function safeStore(key, data) {
try {
// 尝试 IndexedDB(使用 idb 库)
const db = await openDB('MyCache', 1, {
upgrade(db) {
db.createObjectStore('cache');
}
});
await db.put('cache', data, key);
return;
} catch (e) {
console.warn('IndexedDB 不可用,降级到 localStorage', e);
try {
localStorage.setItem(key, JSON.stringify(data));
} catch (storageErr) {
console.error('存储完全失败', storageErr);
// 最终降级:内存变量或提示用户
}
}
}