浏览器存储是前端开发中最常打交道的底层能力之一。从早期只有 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 的典型用途:
- 多步骤表单的中间状态
- 单次会话内的临时导航状态
- 防止页面刷新丢失的临时数据
使用注意:
- 同步阻塞:在大量数据读写时会阻塞主线程。不要在主线程中循环写入大数组。
- 仅支持字符串:存储对象需
JSON.stringify,取出需JSON.parse,大对象序列化有性能成本。 - 同源策略:不同子域之间不共享,
a.example.com和b.example.com各有独立的存储空间。 - 隐私模式限制:部分浏览器隐私模式下 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 基于事件回调,原生写法较为冗长。实际项目中通常使用 idb、Dexie.js 等封装库来简化操作。
五、选型决策树
面对具体需求时,可以按以下顺序判断:
- 服务端是否需要读取? 是 → Cookie(配合 HttpOnly/Secure/SameSite)。
- 数据是否仅在当前标签页会话有效? 是 → SessionStorage。
- 数据量是否超过 5MB,或需要索引查询、存储二进制? 是 → IndexedDB。
- 其余前端键值对场景 → LocalStorage。
再补充几条实战经验:
- 敏感令牌不要放 LocalStorage:XSS 可直接读取。优先使用 HttpOnly Cookie,或内存 + 刷新令牌方案。
- 不要用 LocalStorage 做缓存层:它没有过期机制,需要手动管理失效逻辑,容易积累脏数据。
- IndexedDB 适合“数据层”而非“状态层”:应用运行时状态应放在内存(如 Redux/Pinia),IndexedDB 负责持久化。
- 容量不是唯一考量:同步阻塞、序列化成本、跨标签页同步(
storage事件)都会影响体验。
六、总结
四种存储方案并非互相替代,而是各司其职:
- Cookie 解决“服务端要读”的问题;
- LocalStorage / SessionStorage 解决“前端简单键值持久化”的问题;
- IndexedDB 解决“前端大量结构化数据持久化”的问题。
选型的核心是三个问题:谁需要读这份数据?数据有多大?需要保存多久? 回答清楚这三点,方案自然浮出水面。
未经允许不得转载:任鹏个人博客 » Cookie、LocalStorage、SessionStorage 与 IndexedDB 选型指南


朋友圈点赞图在线生成源码