跳到主内容

2

浏览器存储

Cookie、localStorage、sessionStorage、IndexedDB、Cache API 各自的边界

必学5 分钟

选错存储是上线后才发现的高频坑。这一节给你一张选型地图。

浏览器给前端提供了 5 种主流的"存数据"方式,各自有完全不同的容量、 生命周期、访问方式和安全特性。新人最常见的错是把所有数据都丢进 localStorage——能跑,但隐患多。


1. 一张表:全部对比

存储容量量级是否随请求自动发送生命周期同步还是异步同源/跨域规则
Cookie~4 KB / 条✅ 每个匹配请求都带可设过期 / 会话同步受 domain / path / SameSite 控制
localStorage~5–10 MB / 源永久(除非主动清)同步严格同源
sessionStorage~5 MB / 源 / 标签标签关闭就消失同步严格同源 + 同标签
IndexedDB~ 几百 MB+永久(除非主动清)异步严格同源
Cache API取决于平台由 Service Worker 管异步严格同源

2. Cookie:唯一会"自动跟随请求"的存储

服务端通过 Set-Cookie 响应头种 cookie:

Set-Cookie: sid=abc123; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=86400

每个属性的含义:

属性作用
Path只在指定 path 下发送
Domain控制是否带到子域;不写默认只当前域
HttpOnlyJS 读不到(防 XSS 偷 cookie);这是会话 cookie 的默认要求
Secure只在 HTTPS 下发送
SameSite跨站请求是否携带(Strict / Lax / None),防 CSRF 主力
Max-Age秒数;不写就是会话 cookie(关浏览器消失)
Expires具体过期时间(与 Max-Age 二选一)

2.2 SameSite 必须懂

取值行为
Strict跨站请求绝不携带——最严格,但用户从外站点链接进来可能登录态丢失
Lax跨站 GET 顶级导航(<a> 点击)会带,POST / iframe / Ajax 不带——浏览器默认值
None任何跨站都带;必须配 Secure——常用于第三方嵌入场景

写认证 cookie 的标准姿势是 HttpOnly; Secure; SameSite=Lax。这是 §2.6 安全节的反复回链。

浏览器原生 document.cookie API 又难用又同步——只能拿到非 HttpOnly 的 cookie,且字符串需要手动 parse。业务里直接用就是踩坑

现代写法:

  • 客户端不读认证态——后端给一个 /api/me 接口返回当前用户
  • 真要读,用包装库(js-cookie)或直接走 Cookie Store API(异步、有兼容性顾虑)

3. localStorage / sessionStorage:简单但坑多

localStorage.setItem("theme", "dark");
localStorage.getItem("theme"); // "dark"
localStorage.removeItem("theme");
localStorage.clear();

接口简单。踩坑全在它"看似简单"的地方

后果
同步 API大量读写阻塞主线程;超过 5MB 量级就明显
只能存字符串必须 JSON.stringify / JSON.parse,对象引用、Date / Map 都丢
任何 JS 都能读XSS 可以一把偷走——绝对不要存 token / 密钥
跨标签同步靠 storage 事件同一标签内修改不触发;只有"另一标签"修改才触发 storage 事件
隐私模式下可能被禁用或额度极低调用就抛 QuotaExceededError,要做 try / catch
跨子域不通app.example.comwww.example.com 的 localStorage 互不可见

sessionStorage 区别只有"标签关了就消失"——临时表单草稿、单次会话状态 适合用它。

该用 localStorage 的场景

  • 用户偏好(主题、语言、布局)——少量、可丢、非敏感
  • UI 状态记忆(侧栏展开、最近选择)
  • 离线表单草稿(搭配 beforeunload

不该用的场景

  • 认证 token(用 HttpOnly Cookie)
  • 大量数据(用 IndexedDB)
  • 跨标签实时通信(用 BroadcastChannel

设计判断(内聚边界):选存储不是按"哪个 API 顺手",是按"数据的 生命周期 + 访问模式"。会话级 → sessionStorage;跨会话偏好 → localStorage; 大量结构化 → IndexedDB;要随请求发 → Cookie。这就是内聚(cohesion) 的核心心智——按职责切,不按熟悉度切。


4. IndexedDB:浏览器里的"数据库"

异步 + 事务 + 索引 + 大容量。能力强,原生 API 难用——业务里推荐用 idbDexie 包装。

import { openDB } from "idb";
 
const db = await openDB("notes", 1, {
  upgrade(db) {
    db.createObjectStore("items", { keyPath: "id" });
  },
});
 
await db.put("items", { id: 1, title: "hello" });
await db.get("items", 1);

何时用:

  • 离线优先应用(PWA 笔记应用、聊天应用)——本地存几千条记录
  • 大型文件 / Blob 缓存(图片、PDF 草稿)
  • 复杂查询(有 index 的快速 lookup)

何时用:

  • 简单键值对(用 localStorage)
  • 临时数据(用内存或 sessionStorage)
  • 需要随请求发送的数据(用 Cookie)

IndexedDB 的"version migration"

升级 schema 必须改 version 数字 + 在 upgrade 回调里手写迁移—— 这是数据库设计判断(D2 + D3 都不过),上线前必须有完整方案。AI 能写 单个迁移 step,但整体迁移策略要你定。


5. Cache API:与 Service Worker 配套

caches 是给 Service Worker 用的"请求-响应缓存"。手动用得不多,多 作为 PWA 离线策略的底层。

const cache = await caches.open("v1");
await cache.put(request, response);
const cached = await cache.match(request);

Service Worker 完整故事在第 8 章前沿话题展开。


6. 选型决策表

来了一段需求"我要在浏览器存 X",照下面问:

问题是 → 选否 → 下一问
要随请求自动发到服务端?Cookie下一问
标签关了就该消失?sessionStorage下一问
单条数据 < 5KB 且总量 < 5MB?localStorage(注意安全!)下一问
结构化数据 + 大量条目 + 复杂查询?IndexedDB(用 idb 包装)下一问
是 Service Worker 拦的请求响应?Cache API重新评估需求

7. AI 在存储这层能帮你做什么

任务Tier备注
写 localStorage / sessionStorage 包装Tier 1包成"对象 → JSON"helper 是入门项目
写 IndexedDB schema + 迁移Tier 3 reviewschema 是 API,发布即难改;迁移更要慎
决定 token 放哪里永不委托安全决策;错了泄漏不可逆(D3 + D5 双红线)
决定哪个数据用哪种存储Tier 4 辅助业务上下文 AI 拿不到,让它列方案、你拍板
设置 Cookie 的 SameSite / Secure / HttpOnlyTier 3 review配置上线前必须自己读一遍 + 跨域场景实测

延伸阅读


下一节:§2.6 Web 安全 —— 同源策略、XSS、CSRF、CSP。