Cookie、LocalStorage、SessionStorage 与 IndexedDB 选型指南

浏览器存储是前端开发中最常打交道的底层能力之一。从早期只有 Cookie 可用,到 HTML5 引入 Web Storage,再到 IndexedDB 提供结构化数据库能力,四种方案各有明确的适用边界。选错存储方案,轻则代码臃肿,重则引发性能瓶颈或安全问题。本文从容量、生命周期、数据模型、同步/异步、网络行为等维度逐一拆解,帮助你做出合理的技术选型。

一、四种存储方案的核心差异

先看一张全局对比表:

特性 Cookie LocalStorage SessionStorage IndexedDB
容量 ~4KB ~5-10MB ~5-10MB 通常数百MB至数GB
生命周期 可设置过期时间 永久,除非手动清除 标签页/窗口关闭即清除 永久,除非手动清除
数据模型 键值对(字符串) 键值对(字符串) 键值对(字符串) 结构化对象、索引、事务
读写方式 同步 同步 同步 异步
是否随请求发送 是(同源请求自动携带)
作用域 同源 + 路径 同源 同源 + 标签页 同源
Web Worker 可用

这张表基本决定了每种方案的“天生用途”。下面逐一展开。

二、Cookie:唯一能自动随请求发送的存储

Cookie 最大的特殊性在于它会自动附加到同源的 HTTP 请求头中。这意味着:

  • 服务端需要读取的数据(如会话标识、用户偏好)适合放 Cookie。
  • 纯前端使用的数据不应放 Cookie,因为每次请求都会白白增加网络开销。

关键属性必须掌握:

  • HttpOnly:禁止 JavaScript 读取,防御 XSS 窃取会话。
  • Secure:仅通过 HTTPS 传输。
  • SameSite:控制跨站请求是否携带,Lax 是当前浏览器默认值,能有效缓解 CSRF。
  • Max-Age / Expires:控制过期时间,不设置则为会话 Cookie。

适用场景: 会话管理(Session ID)、服务端渲染所需的用户偏好、A/B 测试分组标记。

不适用场景: 存储大量前端数据、存储敏感信息(除非配合 HttpOnly + Secure)、高频读写的配置项。

三、LocalStorage 与 SessionStorage:简单的键值存储

两者 API 完全一致,唯一区别是生命周期。它们都是同步 API,数据以字符串形式存储。

LocalStorage 的典型用途:

  • 用户主题偏好(暗色/亮色模式)
  • 不敏感的登录态辅助标记
  • 表单草稿的本地暂存
  • 静态配置缓存

SessionStorage 的典型用途:

  • 多步骤表单的中间状态
  • 单次会话内的临时导航状态
  • 防止页面刷新丢失的临时数据

使用注意:

  1. 同步阻塞:在大量数据读写时会阻塞主线程。不要在主线程中循环写入大数组。
  2. 仅支持字符串:存储对象需 JSON.stringify,取出需 JSON.parse,大对象序列化有性能成本。
  3. 同源策略:不同子域之间不共享,a.example.comb.example.com 各有独立的存储空间。
  4. 隐私模式限制:部分浏览器隐私模式下 LocalStorage 可能不可写,需做容错处理。
// 安全的读写封装示例
function safeSetItem(key, value) {
  try {
    localStorage.setItem(key, JSON.stringify(value));
  } catch (e) {
    if (e.name === 'QuotaExceededError') {
      console.warn('存储空间已满');
    }
  }
}

四、IndexedDB:浏览器中的结构化数据库

IndexedDB 是一个运行在浏览器中的事务型 NoSQL 数据库。它的核心优势在于:

  • 大容量:通常可达磁盘空间的 50% 以上(因浏览器而异)。
  • 异步 API:不会阻塞主线程。
  • 索引与查询:支持创建索引,按范围查询,而不仅是键值查找。
  • 存储二进制:可直接存储 Blob、ArrayBuffer、File 等。
  • 事务支持:保证数据一致性。

典型适用场景:

  • PWA 离线应用的数据缓存(如邮件客户端缓存邮件体)
  • 大量结构化数据的本地存储(如笔记应用、待办列表)
  • 需要按条件检索的本地数据集
  • 存储文件、图片等二进制资源

基本使用模式:

const request = indexedDB.open('MyDB', 1);

request.onupgradeneeded = (event) => {
  const db = event.target.result;
  const store = db.createObjectStore('notes', { keyPath: 'id', autoIncrement: true });
  store.createIndex('by_date', 'createdAt');
};

request.onsuccess = (event) => {
  const db = event.target.result;
  const tx = db.transaction('notes', 'readwrite');
  const store = tx.objectStore('notes');
  store.add({ title: '示例笔记', createdAt: Date.now() });
};

使用代价: API 基于事件回调,原生写法较为冗长。实际项目中通常使用 idbDexie.js 等封装库来简化操作。

五、选型决策树

面对具体需求时,可以按以下顺序判断:

  1. 服务端是否需要读取? 是 → Cookie(配合 HttpOnly/Secure/SameSite)。
  2. 数据是否仅在当前标签页会话有效? 是 → SessionStorage。
  3. 数据量是否超过 5MB,或需要索引查询、存储二进制? 是 → IndexedDB。
  4. 其余前端键值对场景 → LocalStorage。

再补充几条实战经验:

  • 敏感令牌不要放 LocalStorage:XSS 可直接读取。优先使用 HttpOnly Cookie,或内存 + 刷新令牌方案。
  • 不要用 LocalStorage 做缓存层:它没有过期机制,需要手动管理失效逻辑,容易积累脏数据。
  • IndexedDB 适合“数据层”而非“状态层”:应用运行时状态应放在内存(如 Redux/Pinia),IndexedDB 负责持久化。
  • 容量不是唯一考量:同步阻塞、序列化成本、跨标签页同步(storage 事件)都会影响体验。

六、总结

四种存储方案并非互相替代,而是各司其职:

  • Cookie 解决“服务端要读”的问题;
  • LocalStorage / SessionStorage 解决“前端简单键值持久化”的问题;
  • IndexedDB 解决“前端大量结构化数据持久化”的问题。

选型的核心是三个问题:谁需要读这份数据?数据有多大?需要保存多久? 回答清楚这三点,方案自然浮出水面。

未经允许不得转载:任鹏个人博客 » Cookie、LocalStorage、SessionStorage 与 IndexedDB 选型指南

赞 (0) 打赏

评论 0

取消
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏