浏览器存储技术深度解析:cookie、localStorage、sessionStorage、IndexedDB 与 WebSQL

154 阅读6分钟

一、核心特性对比表

特性/技术CookielocalStoragesessionStorageIndexedDBWebSQL (已废弃)
出现时间1993年2009年2009年2015年2009年
推动标准/公司网景公司 (Lou Montulli)HTML5规范HTML5规范W3C标准HTML5草案
技术背景解决HTTP无状态问题,实现会话管理解决Cookie容量小和性能问题解决Cookie容量小和性能问题替代WebSQL,支持非关系型数据和大容量存储引入关系型数据库能力
最大容量4KB5-10MB5-10MB50%磁盘空间5-50MB
生命周期可设置过期时间永久存储标签页关闭清除永久存储永久存储
数据格式字符串字符串字符串结构化数据/二进制关系型数据
服务器访问✅ 每次请求自动携带
同源共享全域名全域名单标签页全域名全域名
事务支持
查询能力键查找键查找键查找高级索引查询SQL查询
异步操作
浏览器支持所有浏览器所有现代浏览器所有现代浏览器所有现代浏览器仅WebKit内核
标准状态标准化HTML5标准HTML5标准W3C标准已废弃
适用场景会话管理/身份认证用户偏好设置表单草稿/临时状态离线应用/大型数据不应使用

二、详细技术解析

1. Cookie

核心特性

  • 容量限制:每个Cookie不超过4KB,每个域名下最多允许20-50个Cookie(取决于浏览器)
  • 生命周期:可设置过期时间(ExpiresMax-Age),未设置则为会话Cookie(关闭浏览器即失效)
  • 作用域:通过DomainPath属性控制可见性
  • 自动传输:每次HTTP请求都会自动携带同域Cookie,可通过Secure(仅HTTPS)和HttpOnly(禁止JS访问)增强安全
  • 同步API:通过document.cookie读写,操作可能阻塞主线程
// 设置Cookie
document.cookie = "username=john_doe; expires=Fri, 31 Dec 2023 23:59:59 GMT; path=/";

// 读取Cookie
function getCookie(name) {
  const cookies = document.cookie.split('; ');
  for (const cookie of cookies) {
    const [key, value] = cookie.split('=');
    if (key === name) return decodeURIComponent(value);
  }
  return null;
}

// 删除Cookie
document.cookie = "username=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/";

最佳实践

  • 仅用于会话管理(如存储Session ID)
  • 敏感数据设置HttpOnlySecure属性
  • 使用SameSite属性防御CSRF攻击(推荐LaxStrict
  • 避免存储大数据(影响请求性能)

2. localStorage

核心特性

  • 持久存储:数据永久保存,除非主动清除
  • 同源策略:相同协议+域名+端口共享数据
  • 同步API:可能阻塞主线程
  • 存储格式:仅字符串(需序列化对象)
  • 容量:5-10MB(不同浏览器有差异)
// 基础操作
localStorage.setItem('theme', 'dark'); // 存储
const theme = localStorage.getItem('theme'); // 读取
localStorage.removeItem('theme'); // 删除
localStorage.clear(); // 清空

// 存储对象
const user = { id: 1, name: 'John' };
localStorage.setItem('user', JSON.stringify(user));
const storedUser = JSON.parse(localStorage.getItem('user'));

最佳实践

  • 存储用户偏好设置(主题/语言等)
  • 配合 storage 事件实现跨标签页通信
  • 敏感数据需加密存储
  • 避免存储 >1MB 数据

3. sessionStorage

核心特性

  • 会话级存储:关闭标签页自动清除数据
  • 标签页隔离:不同标签页不共享数据
  • 刷新保留:页面刷新不丢失数据
  • 同步API:操作方式同localStorage
  • 容量:5-10MB
// 表单数据临时存储
document.getElementById('form').addEventListener('input', (e) => {
  sessionStorage.setItem(e.target.name, e.target.value);
});

// 页面恢复时填充
window.addEventListener('load', () => {
  const inputs = document.querySelectorAll('input');
  inputs.forEach(input => {
    const savedValue = sessionStorage.getItem(input.name);
    if(savedValue) input.value = savedValue;
  });
});

典型场景

  • 多步骤表单草稿保存
  • 单页应用(SPA)路由状态保持
  • 敏感操作临时令牌存储

4. IndexedDB

核心架构

image.png

核心特性

  • 非关系型数据库:支持键值对、文档和二进制数据
  • 异步API:所有操作非阻塞
  • 事务支持:原子性操作保障数据一致性
  • 索引查询:支持高性能复杂查询
  • 大容量:可达磁盘空间的50%(通常 >250MB)
// 打开/创建数据库
const request = indexedDB.open('myDB', 2);

request.onupgradeneeded = (event) => {
  const db = event.target.result;
  if (!db.objectStoreNames.contains('users')) {
    const store = db.createObjectStore('users', { keyPath: 'id' });
    store.createIndex('name_idx', 'name', { unique: false });
  }
};

// 添加数据
async function addUser(user) {
  const db = await request;
  const tx = db.transaction('users', 'readwrite');
  await tx.store.add(user);
  await tx.done;
}

// 查询数据
async function getUser(id) {
  const db = await request;
  const tx = db.transaction('users', 'readonly');
  return tx.store.get(id);
}

高级功能

  • 游标遍历大数据集
  • 二进制文件存储(Blob/ArrayBuffer)
  • 复合索引多条件查询
  • 版本迁移管理

5. WebSQL(已废弃)

核心特性

  • 关系型数据库:基于SQLite实现
  • SQL语法:支持SELECT/INSERT/UPDATE等操作
  • 异步API:通过事务执行SQL
  • 容量:5-50MB(浏览器限制)
// 创建数据库(已废弃,仅作参考)
const db = openDatabase('mydb', '1.0', 'My DB', 2 * 1024 * 1024);

// 执行SQL
db.transaction(tx => {
  tx.executeSql('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT)');
  tx.executeSql('INSERT INTO users (name) VALUES (?)', ['Alice']);
  tx.executeSql('SELECT * FROM users', [], (_, results) => {
    for (let i = 0; i < results.rows.length; i++) {
      console.log(results.rows.item(i));
    }
  });
});

致命缺陷

  • ⚠️ 已被W3C正式废弃(2010年)
  • ⚠️ 仅WebKit内核浏览器支持(Chrome/Safari)
  • ⚠️ 安全漏洞风险高
  • ⚠️ 无跨浏览器兼容性

三、场景化选型指南

1. 🍪 必须选择 Cookie 的场景

  • 用户身份认证:存储会话ID(配合HttpOnly+Secure

    Set-Cookie: session_id=abc123; HttpOnly; Secure; SameSite=Lax
    
  • 跨子域共享数据:设置顶级域名作用域

    document.cookie = "pref_lang=en; domain=.example.com; path=/";
    
  • 符合GDPR的跟踪(需用户同意后设置)

2. 💾 选择 localStorage 的场景

  • 用户偏好设置持久化

    // 保存主题偏好
    localStorage.setItem('theme', 'dark');
    
    // 读取设置
    const theme = localStorage.getItem('theme') || 'light';
    
  • 静态资源缓存(配合版本控制)

    const cacheVersion = 'v2';
    if (localStorage.getItem('cacheVer') !== cacheVersion) {
      localStorage.clear();
      localStorage.setItem('cacheVer', cacheVersion);
    }
    

3. 🚀 选择 sessionStorage 的场景

  • 单页应用(SPA)路由状态保持

    // 保存当前页面状态
    window.addEventListener('beforeunload', () => {
      sessionStorage.setItem('formData', JSON.stringify(formState));
    });
    
  • 敏感操作临时令牌存储

    // 存储两步验证令牌(会话级安全)
    sessionStorage.setItem('mfa_token', temporaryToken);
    
  • 防止表单重复提交

    if (!sessionStorage.getItem('formSubmitted')) {
      submitForm();
      sessionStorage.setItem('formSubmitted', 'true');
    }
    

4. 🗃️ 必须选择 IndexedDB 的场景

  • 离线优先应用数据存储

    // 保存文章草稿
    const db = await openDB('NotesDB', 1, {
      upgrade(db) {
        db.createObjectStore('drafts', { keyPath: 'id' });
      }
    });
    
    await db.put('drafts', {
      id: Date.now(),
      title: '未命名文档',
      content: '...',
      updated: new Date()
    });
    
  • 大型数据集缓存(>10MB)

    // 存储产品目录
    const products = await fetch('/api/products');
    const tx = db.transaction('products', 'readwrite');
    const store = tx.objectStore('products');
    products.forEach(p => store.put(p));
    
  • 文件/二进制数据处理

    // 存储用户上传的文件
    const file = input.files[0];
    const buffer = await file.arrayBuffer();
    
    await db.put('userFiles', {
      id: file.name,
      data: buffer,
      type: file.type,
      size: file.size
    });
    

5. ⚠️ 避免使用 WebSQL 的场景

所有新项目都应避免使用 WebSQL,因为:

  • 已被 W3C 正式弃用
  • 仅支持 WebKit 内核浏览器
  • 无事务隔离级别保证
  • 被 IndexedDB 全面超越

 终极选型原则

  1. 会话管理 → Cookie(HttpOnly+Secure
  2. 小量持久数据 → localStorage
  3. 临时会话数据 → sessionStorage
  4. 大型/结构化数据 → IndexedDB
  5. 文件/二进制数据 → IndexedDB + OPFS
  6. 关系型数据 → 服务端数据库 + IndexedDB缓存
  7. 任何新项目 → 禁止使用WebSQL

决策口诀

认证会话用Cookie,
用户设置localStorage;
草稿状态session,
大型数据IndexedDB;
WebSQL已作古,
未来拥抱OPFS!

四、混合存储策略示例

电商网站最佳实践

deepseek_mermaid_20250623_3299f3.png

代码实现

// 混合存储管理器
class StorageManager {
  constructor() {
    this.authToken = null;
  }

  // 初始化存储
  async init() {
    this.authToken = this.getCookie('session_token');
    
    // 初始化IndexedDB
    this.db = await openDB('EcommerceDB', 2, {
      upgrade(db) {
        if (!db.objectStoreNames.contains('cart')) {
          db.createObjectStore('cart', { keyPath: 'sku' });
        }
        if (!db.objectStoreNames.contains('catalog')) {
          const store = db.createObjectStore('catalog', { keyPath: 'id' });
          store.createIndex('category_idx', 'category');
        }
      }
    });
  }

  // Cookie操作
  getCookie(name) {
    /* ... */ 
  }

  // 保存购物车
  async saveCart(items) {
    const tx = this.db.transaction('cart', 'readwrite');
    await Promise.all([
      ...items.map(item => tx.store.put(item)),
      tx.done
    ]);
    
    // 同步备份到sessionStorage
    sessionStorage.setItem('cart_last_updated', Date.now());
  }

  // 保存用户设置
  savePreferences(prefs) {
    localStorage.setItem('user_prefs', JSON.stringify(prefs));
  }
}