浏览器存储方案对比深度解析
面试常考浏览器存储全家桶:localStorage、sessionStorage、Cookie、IndexedDB 的容量、生命周期、同源与读写差异。 讲清各自适用场景与选型,能答出「什么时候用哪个」即可过关。
一句话概括
Cookie 不是”存储”,是 HTTP 无状态的身份补丁——它设计出来就一个使命:让每次请求自动带上身份信息。localStorage 存偏好,sessionStorage 管隔离,IndexedDB 当数据库。四个全用、各司其职,才是上线应用的常态。
核心知识点
1. Cookie — 自动传输是超能力,4KB 是保护(不是缺陷)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Cookie 的价值是 Transport(自动随请求传输),不是 Storage(存数据)
// 后端种身份 Cookie:
// Set-Cookie: session_id=abc123; HttpOnly; Secure; SameSite=Lax; Max-Age=3600; Path=/
// 前端只能读写非 HttpOnly 的 Cookie
document.cookie = 'theme=dark; Max-Age=2592000; Path=/; Secure; SameSite=Lax';
// ⚠️ 经典面试坑:document.cookie 返回的是拼起来的字符串,不是对象!
console.log(document.cookie); // "theme=dark; lang=zh"
// 手写解析(注意 value 里可能有 =,用 indexOf 定位第一个 = 切分)
const parseCookies = str =>
Object.fromEntries(
str.split('; ').map(pair => {
const i = pair.indexOf('=');
return [pair.slice(0, i), pair.slice(i + 1)];
})
);
核心定位: Cookie 的 4KB 不是 bug,是 feature——它应该只存 sessionId,几十字节足够。存多了每次 HTTP 请求都带着,白白拖慢所有请求。
2. localStorage — 持久化键值对(~5MB),同步 API
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 同源所有标签页共享、永久存在、同步读写
localStorage.setItem('prefs', JSON.stringify({ theme: 'dark', fontSize: 16 }));
// 🔥 面试高频:给 localStorage 加过期时间
const smartStorage = {
set(key, value, ttlMs = Infinity) {
localStorage.setItem(key, JSON.stringify({
value,
expires: Date.now() + ttlMs
}));
},
get(key) {
const raw = localStorage.getItem(key);
if (!raw) return null;
const { value, expires } = JSON.parse(raw);
if (Date.now() > expires) { localStorage.removeItem(key); return null; }
return value;
}
};
// storage 事件:其他标签页写入时触发(自己写自己不触发)
window.addEventListener('storage', ({ key, newValue }) => {
if (key === 'auth_token' && !newValue) {
// 另一个标签页退登了,本标签页跟着刷新
location.reload();
}
});
3. sessionStorage — 标签页级沙箱(~5MB)
1
2
3
4
5
6
7
8
sessionStorage.setItem('draft', JSON.stringify({ title: '草稿', content: '' }));
// 核心行为演示:
// 标签页 A:sessionStorage.setItem('x', 'A')
// 标签页 B(同源同 URL):sessionStorage.getItem('x') // null!
//
// ⚠️ 注意:Ctrl/Cmd+N 新窗口 = 全新 session,不继承任何 sessionStorage
// Chrome 崩溃恢复标签页会恢复 sessionStorage,Firefox 不一定——别依赖这个
4. IndexedDB — 浏览器里的异步数据库(数百 MB+)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 原生 API 回调地狱,生产用 idb 库(仅 1.3KB)
import { openDB } from 'idb';
const db = await openDB('MyApp', 1, {
upgrade(db) {
const store = db.createObjectStore('messages', {
keyPath: 'id',
autoIncrement: true
});
store.createIndex('by_time', 'timestamp');
}
});
// 写入
await db.add('messages', { text: 'Hello', timestamp: Date.now() });
// 索引查询:最近 24 小时的消息
const recent = await db.getAllFromIndex('messages', 'by_time',
IDBKeyRange.lowerBound(Date.now() - 86400000));
什么时候用 IndexedDB 而非 localStorage:
- 数据 > 几十 MB(localStorage 硬上限 ~5MB)
- 需要按条件查询(localStorage 只能全量遍历
Object.keys) - 存 Blob / File / ArrayBuffer(localStorage 只能字符串)
- 需要事务保证写入原子性
5. 现代补充:CacheStorage 和 OPFS
1
2
3
4
5
6
7
8
9
// CacheStorage:Service Worker 专用,缓存静态资源,PWA 秒开靠它
const cache = await caches.open('v1');
await cache.addAll(['/', '/styles.css', '/app.js']);
// OPFS(Origin Private File System):大文件高性能读写
// SQLite WASM、视频编辑器都在用
const root = await navigator.storage.getDirectory();
const fileHandle = await root.getFileHandle('data.bin', { create: true });
const writable = await fileHandle.createSyncAccessHandle(); // 同步写,极快
决策速查
| Cookie | localStorage | sessionStorage | IndexedDB | |
|---|---|---|---|---|
| 容量 | 4KB | ~5MB | ~5MB | 数百MB+ |
| 随请求发送 | ✅ 自动 | ❌ | ❌ | ❌ |
| 生命周期 | 可设过期 | 永久 | 标签页关闭 | 永久 |
| API 类型 | 同步 | 同步 | 同步 | 异步 |
| 事务 | ❌ | ❌ | ❌ | ✅ |
| 存二进制 | ❌ | ❌ | ❌ | ✅ |
其实你每天都在用
- 登录态:sessionId 在 HttpOnly Cookie 里,你从不手动传它,后端却每次都认得你
- 暗色模式无闪烁:刷新前先从 localStorage 读
theme,设置<html data-theme>再渲染——否则闪白屏 - 评论草稿救场:写了半小时的评论不小心关了标签页——sessionStorage 里的草稿帮你恢复(注意是 sessionStorage,关标签页才清)
- Google Docs 离线编辑:所有文档内容存 IndexedDB,断网照写不误,联网后增量同步
- PWA 秒开:Service Worker + CacheStorage 缓存整个应用的 HTML/JS/CSS,第二次打开不依赖网络
常见误解(FAQ)
❌ 误区:「Token 存 localStorage 最方便,大家都这么干」 任何 XSS 注入的
<script>一行localStorage.getItem('token')就能偷走。正确姿势:access token 存内存变量(刷新需重新登录),refresh token 走 HttpOnly + Secure + SameSite=Strict Cookie。方便不是安全的挡箭牌。❌ 误区:「SameSite=Strict 一定比 Lax 安全」 Strict 确实更严,但用户从邮件 / GitHub 点链接跳转到你的网站时 Cookie 不发送——永远是”未登录”状态。Lax 在安全与可用间取得平衡:允许安全的顶层 GET 导航携带 Cookie,阻止跨站 POST 等危险请求。
❌ 误区:「sessionStorage 同源标签页就能共享」 绝对不能。这是它最核心的设计——每个标签页独立 session。两个标签页打开同一 URL,sessionStorage 完全隔离。跨标签页通信请用
BroadcastChannel或localStorage的storage事件。❌ 误区:「数据量大就上 IndexedDB」 IndexedDB 的异步模型让代码复杂度陡增。5MB 以内用 localStorage + 过期封装完全够用。别为了用 IndexedDB 而用 IndexedDB——它解决的是”海量结构化数据 + 索引查询”这个特定问题。
一句话总结
选存储方案不是比谁容量大:Cookie 补 HTTP 无状态的坑,localStorage 存偏好,sessionStorage 做标签页隔离,IndexedDB 当本地数据库。面试官问”区别”,想听的是你有没有理解每种方案为什么被设计出来。