浏览器存储方式

现代浏览器提供了多种存储机制,用于在客户端保存数据。根据数据的生命周期、大小限制和使用场景,开发者需要选择最适合的方案。以下是主流存储方式的详细对比与解析。

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. 综合对比表

特性CookieLocalStorageSessionStorageIndexedDB
数据生命周期可设置过期时间,默认关闭浏览器失效永久有效,直到手动删除仅在当前标签页会话有效永久有效,直到手动删除
存储大小~4KB~5MB~5MB很大 (>250MB)
与服务器通信每次 HTTP 请求都会自动携带不参与服务器通信不参与服务器通信不参与服务器通信
易用性较差 (原生 API 难用)简单 (Key-Value)简单 (Key-Value)复杂 (类似数据库操作)
访问性能同步 (且影响网络带宽)同步 (大数据量可能阻塞 UI)同步异步 (高性能)
主要用途身份验证、Session ID简单配置、持久化数据临时表单、单次会话状态复杂结构化数据、离线应用

总结建议

  1. 需要与服务端交互的 Auth Token:优先考虑 Cookie (配合 HttpOnly)。
  2. 简单的键值对数据 (设置、标志位):使用 LocalStorage。
  3. 临时保存的数据 (刷新页面还在,关闭页面清除):使用 SessionStorage。
  4. 大量结构化数据、文件、离线数据:务必使用 IndexedDB,避免 LocalStorage 阻塞主线程。