浏览器存储方式
现代浏览器提供了多种存储机制,用于在客户端保存数据。根据数据的生命周期、大小限制和使用场景,开发者需要选择最适合的方案。以下是主流存储方式的详细对比与解析。
1. Cookie
Cookie 是最早期的客户端存储机制,主要设计初衷是为了在无状态的 HTTP 协议中维持状态(如用户登录态)。
特点
- 大小限制:非常小,通常限制在 4KB 左右。
- 自动发送:在同域名的 HTTP 请求中,Cookie 会自动附加在 Request Header 中发送给服务器(这可能造成性能浪费)。
- 配置项:
Expires/Max-Age:设置过期时间。HttpOnly:若设置为 true,JavaScript 无法读取(防止 XSS 攻击窃取 Cookie)。Secure:只允许在 HTTPS 下传输。SameSite:控制跨站请求时是否发送 Cookie(防止 CSRF 攻击)。
适用场景
- 存储用户 Token / Session ID(配合
HttpOnly)。 - 需要服务端读取的小型配置信息。
代码示例
text
123456789101112131415161718192021222324
/**
* 设置 Cookie 的完整示例
* 注意:HttpOnly 属性只能由服务器设置,无法在客户端 JS 中设置
*/
// 1. 基础用法:设置路径 (Path)
document.cookie = "username=JohnDoe; path=/";
// 2. 设置过期时间 (Max-Age 或 Expires)
// Max-Age: 单位为秒 (例如 3600秒 = 1小时)
document.cookie = "token=abc12345; path=/; max-age=3600";
// Expires: 具体的 UTC 时间字符串
const date = new Date();
date.setTime(date.getTime() + (7 * 24 * 60 * 60 * 1000)); // 7天后
document.cookie = `theme=dark; path=/; expires=${date.toUTCString()}`;
// 3. 安全配置 (Secure & SameSite)
// Secure: 仅允许在 HTTPS 连接中传输
// SameSite: Strict (完全禁止跨站) | Lax (导航到目标网址时发送) | None (允许跨站,必须配合 Secure)
document.cookie = "tracking_id=xyz987; path=/; Secure; SameSite=Strict";
// 4. 读取 Cookie (需要自行解析)
console.log(document.cookie); // 输出: "username=JohnDoe; token=abc12345; ..."
2. Web Storage (LocalStorage & SessionStorage)
HTML5 引入了 Web Storage API,旨在提供更大容量且更易用的本地存储,且不会自动随 HTTP 请求发送给服务器。
A. LocalStorage (本地存储)
- 生命周期:永久有效。除非用户手动清除或代码调用清除,否则数据一直存在。
- 作用域:同源(协议、域名、端口相同)共享。
- 大小限制:通常为 5MB 左右(不同浏览器略有差异)。
B. SessionStorage (会话存储)
- 生命周期:仅在当前会话(Session)有效。当浏览器标签页或窗口关闭时,数据会被清除。
- 注意:在同一个窗口打开新链接(
_self)会保留,但右键“在新标签页打开”或通过window.open打开的新标签页(取决于浏览器实现和上下文)通常不共享 SessionStorage。
- 注意:在同一个窗口打开新链接(
- 作用域:限制在当前标签页,即使是同源的两个标签页,SessionStorage 也是隔离的。
- 大小限制:通常为 5MB 左右。
适用场景
- LocalStorage: 网站偏好设置(如深色模式)、购物车数据(未登录状态)、长期缓存的数据。
- SessionStorage: 表单分步填写的临时数据、一次性敏感信息(关闭页面即焚)。
代码示例 (两者 API 相同)
text
123456789101112131415
// API 非常简单,没有复杂的配置项
// 1. 存储数据 (Key-Value)
localStorage.setItem('theme', 'dark');
localStorage.setItem('userSettings', JSON.stringify({ notifications: true, volume: 80 })); // 存对象需序列化
// 2. 读取数据
const theme = localStorage.getItem('theme');
const settings = JSON.parse(localStorage.getItem('userSettings') || '{}');
// 3. 删除特定项
localStorage.removeItem('theme');
// 4. 清空所有数据
localStorage.clear();
3. IndexedDB
IndexedDB 是一个运行在浏览器中的非关系型数据库 (NoSQL)。它比 Web Storage 强大得多,适合存储大量结构化数据。
特点
- 容量大:通常不少于 250MB,甚至可以达到磁盘可用空间的百分比(如 50%)。
- 异步操作:所有操作(读取、写入)都是异步的,不会阻塞主线程(LocalStorage 是同步的,大数据量读取会卡顿)。
- 支持事务:支持事务(Transaction),保证数据一致性。
- 支持索引:可以给字段建立索引,进行高效查询。
- 支持二进制存储:可以存储
ArrayBuffer、Blob等文件对象。
适用场景
- 离线应用(PWA)的大量数据存储。
- 富文本编辑器保存草稿、图片或文件缓存。
- 复杂的本地数据管理系统。
代码示例 (配置数据库结构)
text
12345678910111213141516171819202122232425262728293031323334353637383940
// 打开数据库: open(数据库名, 版本号)
const request = indexedDB.open("MyDatabase", 1);
// 配置阶段:仅在数据库版本升级(第一次打开或版本号增加)时触发
request.onupgradeneeded = function(event) {
const db = event.target.result;
// 1. 创建对象仓库 (类似于 SQL 中的 Table)
// 配置项: keyPath (主键字段), autoIncrement (是否自增)
if (!db.objectStoreNames.contains("users")) {
const objectStore = db.createObjectStore("users", { keyPath: "id", autoIncrement: true });
// 2. 创建索引 (用于查询)
// createIndex(索引名, 键路径, 配置项)
// unique: false (允许重复值), unique: true (必须唯一,如邮箱)
objectStore.createIndex("name", "name", { unique: false });
objectStore.createIndex("email", "email", { unique: true });
}
};
request.onsuccess = function(event) {
const db = event.target.result;
// 3. 开启事务进行读写
// db.transaction(scope, mode)
// 参数1 ["users"]: 作用域,声明本次事务需要操作哪些表(Object Store)。
// 因为 IndexedDB 建立事务时会锁定这些表,为了性能必须明确指定。
// 参数2 "readwrite": 事务模式,"readonly" (默认,只读) 或 "readwrite" (读写)。
const transaction = db.transaction(["users"], "readwrite");
// 4. 获取具体的对象仓库(表)进行操作
// 必须通过事务对象(transaction)来获取表,而不是直接通过 db 获取
const objectStore = transaction.objectStore("users");
// 添加数据
const addRequest = objectStore.add({ name: "Alice", email: "alice@example.com", age: 25 });
addRequest.onsuccess = () => console.log("数据添加成功");
addRequest.onerror = () => console.error("数据添加失败");
};
4. Cache API
Cache API 最初是为 Service Worker 设计的,用于网络资源的缓存,但也可以在普通页面脚本中使用。
特点
- 基于 Request/Response:专门用于存储网络请求对(Request 对象和 Response 对象)。
- 可编程缓存:开发者可以精确控制缓存哪些资源(CSS, JS, 图片等)以及何时更新。
- 异步:基于 Promise。
适用场景
- PWA (Progressive Web App):实现离线访问。
- 预加载关键资源。
5. 综合对比表
| 特性 | Cookie | LocalStorage | SessionStorage | IndexedDB |
|---|---|---|---|---|
| 数据生命周期 | 可设置过期时间,默认关闭浏览器失效 | 永久有效,直到手动删除 | 仅在当前标签页会话有效 | 永久有效,直到手动删除 |
| 存储大小 | ~4KB | ~5MB | ~5MB | 很大 (>250MB) |
| 与服务器通信 | 每次 HTTP 请求都会自动携带 | 不参与服务器通信 | 不参与服务器通信 | 不参与服务器通信 |
| 易用性 | 较差 (原生 API 难用) | 简单 (Key-Value) | 简单 (Key-Value) | 复杂 (类似数据库操作) |
| 访问性能 | 同步 (且影响网络带宽) | 同步 (大数据量可能阻塞 UI) | 同步 | 异步 (高性能) |
| 主要用途 | 身份验证、Session ID | 简单配置、持久化数据 | 临时表单、单次会话状态 | 复杂结构化数据、离线应用 |
总结建议
- 需要与服务端交互的 Auth Token:优先考虑 Cookie (配合 HttpOnly)。
- 简单的键值对数据 (设置、标志位):使用 LocalStorage。
- 临时保存的数据 (刷新页面还在,关闭页面清除):使用 SessionStorage。
- 大量结构化数据、文件、离线数据:务必使用 IndexedDB,避免 LocalStorage 阻塞主线程。