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):所有读写操作必须处在事务中,事务限定了作用域和模式(readonlyreadwrite)。

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);
      // 最终降级:内存变量或提示用户
    }
  }
}