陵水文章资讯

JavaScript存储方式Cookie到IndexedDB全解析

2026-04-09 11:28:01 浏览次数:0
详细信息
JavaScript存储方式:从Cookie到IndexedDB全解析

1. Cookie

基本概念

Cookie是最传统的客户端存储方式,主要用于会话管理和个性化设置。

// 设置Cookie
document.cookie = "username=John; expires=Fri, 31 Dec 2024 23:59:59 GMT; path=/";
document.cookie = "theme=dark; max-age=2592000; secure; samesite=strict";

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

// 删除Cookie
function deleteCookie(name) {
  document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/`;
}

特点与限制

2. Web Storage (LocalStorage & SessionStorage)

LocalStorage

// 基本操作
localStorage.setItem('user', JSON.stringify({ name: 'Alice', age: 25 }));
const user = JSON.parse(localStorage.getItem('user'));
localStorage.removeItem('user');
localStorage.clear();

// 封装实用类
class LocalStorageManager {
  constructor() {
    this.storage = window.localStorage;
  }

  set(key, value) {
    this.storage.setItem(key, JSON.stringify(value));
    return this;
  }

  get(key) {
    const item = this.storage.getItem(key);
    return item ? JSON.parse(item) : null;
  }

  remove(key) {
    this.storage.removeItem(key);
  }

  clear() {
    this.storage.clear();
  }

  // 监听存储变化
  static addChangeListener(callback) {
    window.addEventListener('storage', (e) => {
      if (e.storageArea === localStorage) {
        callback(e);
      }
    });
  }
}

// 使用示例
const storage = new LocalStorageManager();
storage.set('settings', { theme: 'dark', notifications: true });

SessionStorage

// 使用方法与localStorage相同,但生命周期不同
sessionStorage.setItem('sessionId', 'abc123xyz');
const sessionId = sessionStorage.getItem('sessionId');

// 标签页关闭时自动清除

对比分析

特性 LocalStorage SessionStorage Cookie
容量 5-10MB 5-10MB 4KB
生命周期 永久(除非手动清除) 标签页会话期间 可设置过期时间
自动发送到服务器
访问范围 同源所有标签页 当前标签页 同源所有标签页

3. IndexedDB

概述

IndexedDB是浏览器内置的NoSQL数据库,适合存储大量结构化数据。

基本操作流程

// 数据库操作类
class IndexedDBManager {
  constructor(dbName, version = 1) {
    this.dbName = dbName;
    this.version = version;
    this.db = null;
  }

  // 打开或创建数据库
  open(upgradeCallback) {
    return new Promise((resolve, reject) => {
      const request = indexedDB.open(this.dbName, this.version);

      request.onerror = () => reject(request.error);
      request.onsuccess = () => {
        this.db = request.result;
        resolve(this.db);
      };

      request.onupgradeneeded = (event) => {
        this.db = event.target.result;
        if (upgradeCallback) upgradeCallback(this.db, event);
      };
    });
  }

  // 创建对象仓库(表)
  createObjectStore(storeName, options = { keyPath: 'id', autoIncrement: true }) {
    if (!this.db) throw new Error('Database not opened');

    return this.db.createObjectStore(storeName, options);
  }

  // 添加索引
  createIndex(storeName, indexName, keyPath, options = { unique: false }) {
    const transaction = this.db.transaction([storeName], 'readwrite');
    const store = transaction.objectStore(storeName);
    return store.createIndex(indexName, keyPath, options);
  }

  // 添加数据
  add(storeName, data) {
    return new Promise((resolve, reject) => {
      const transaction = this.db.transaction([storeName], 'readwrite');
      const store = transaction.objectStore(storeName);
      const request = store.add(data);

      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
  }

  // 获取数据
  get(storeName, key) {
    return new Promise((resolve, reject) => {
      const transaction = this.db.transaction([storeName], 'readonly');
      const store = transaction.objectStore(storeName);
      const request = store.get(key);

      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
  }

  // 获取所有数据
  getAll(storeName) {
    return new Promise((resolve, reject) => {
      const transaction = this.db.transaction([storeName], 'readonly');
      const store = transaction.objectStore(storeName);
      const request = store.getAll();

      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
  }

  // 使用索引查询
  getByIndex(storeName, indexName, value) {
    return new Promise((resolve, reject) => {
      const transaction = this.db.transaction([storeName], 'readonly');
      const store = transaction.objectStore(storeName);
      const index = store.index(indexName);
      const request = index.getAll(value);

      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
  }

  // 更新数据
  update(storeName, data) {
    return new Promise((resolve, reject) => {
      const transaction = this.db.transaction([storeName], 'readwrite');
      const store = transaction.objectStore(storeName);
      const request = store.put(data);

      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
  }

  // 删除数据
  delete(storeName, key) {
    return new Promise((resolve, reject) => {
      const transaction = this.db.transaction([storeName], 'readwrite');
      const store = transaction.objectStore(storeName);
      const request = store.delete(key);

      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
  }

  // 清空对象仓库
  clear(storeName) {
    return new Promise((resolve, reject) => {
      const transaction = this.db.transaction([storeName], 'readwrite');
      const store = transaction.objectStore(storeName);
      const request = store.clear();

      request.onsuccess = () => resolve();
      request.onerror = () => reject(request.error);
    });
  }

  // 删除数据库
  static deleteDatabase(dbName) {
    return new Promise((resolve, reject) => {
      const request = indexedDB.deleteDatabase(dbName);

      request.onsuccess = () => resolve();
      request.onerror = () => reject(request.error);
    });
  }
}

完整示例:用户管理系统

// 使用示例
async function setupUserDatabase() {
  const dbManager = new IndexedDBManager('UserDB', 2);

  await dbManager.open((db, event) => {
    // 版本1:创建基础用户表
    if (!db.objectStoreNames.contains('users')) {
      const store = db.createObjectStore('users', { 
        keyPath: 'id',
        autoIncrement: true 
      });

      // 创建索引
      store.createIndex('email', 'email', { unique: true });
      store.createIndex('age', 'age', { unique: false });
      store.createIndex('name', 'name', { unique: false });
    }

    // 版本2:添加用户设置表
    if (event.oldVersion < 2) {
      db.createObjectStore('user_settings', { keyPath: 'userId' });
    }
  });

  return dbManager;
}

// 使用数据库
async function main() {
  try {
    const db = await setupUserDatabase();

    // 添加用户
    await db.add('users', {
      name: 'John Doe',
      email: 'john@example.com',
      age: 30,
      createdAt: new Date()
    });

    // 查询用户
    const users = await db.getAll('users');
    console.log('所有用户:', users);

    // 通过邮箱查询
    const userByEmail = await db.getByIndex('users', 'email', 'john@example.com');
    console.log('通过邮箱查询:', userByEmail);

    // 更新用户
    const userToUpdate = users[0];
    userToUpdate.age = 31;
    await db.update('users', userToUpdate);

  } catch (error) {
    console.error('数据库操作失败:', error);
  }
}

// 事务批处理示例
async function batchOperations(db) {
  const transaction = db.db.transaction(['users', 'user_settings'], 'readwrite');

  const userStore = transaction.objectStore('users');
  const settingsStore = transaction.objectStore('user_settings');

  // 添加用户
  const userRequest = userStore.add({
    name: 'Alice Smith',
    email: 'alice@example.com',
    age: 28
  });

  userRequest.onsuccess = (event) => {
    const userId = event.target.result;

    // 为用户添加设置
    settingsStore.add({
      userId: userId,
      theme: 'dark',
      notifications: true,
      language: 'en'
    });
  };

  return new Promise((resolve, reject) => {
    transaction.oncomplete = () => resolve();
    transaction.onerror = () => reject(transaction.error);
  });
}

4. 存储方案选择指南

决策矩阵

需求场景 推荐方案 原因
会话管理、身份验证 Cookie 自动发送到服务器,支持HttpOnly安全标记
用户偏好设置 LocalStorage 容量适中,持久化存储
单标签页临时数据 SessionStorage 标签页关闭自动清理
少量简单数据 Web Storage API简单,同步操作
大量结构化数据 IndexedDB 容量大,支持索引查询
离线应用数据 IndexedDB + Service Worker 支持离线操作,容量大
二进制数据存储 IndexedDB 支持Blob、ArrayBuffer

容量对比表

存储方式 典型容量 实际限制
Cookie 4KB 每个域名约50个cookie
LocalStorage 5-10MB 不同浏览器有差异
SessionStorage 5-10MB 同LocalStorage
IndexedDB 50%硬盘空间(桌面) 通常至少1GB,可请求更多

5. 最佳实践与注意事项

1. 安全考虑

// 安全存储示例
class SecureStorage {
  constructor() {
    this.encoder = new TextEncoder();
    this.decoder = new TextDecoder();
  }

  // 简单混淆(注意:不是加密)
  async storeSensitiveData(key, data) {
    const jsonStr = JSON.stringify(data);
    const encoded = btoa(unescape(encodeURIComponent(jsonStr)));

    // 在实际应用中,应考虑使用Web Crypto API进行加密
    localStorage.setItem(key, encoded);
  }

  async retrieveSensitiveData(key) {
    const encoded = localStorage.getItem(key);
    if (!encoded) return null;

    try {
      const jsonStr = decodeURIComponent(escape(atob(encoded)));
      return JSON.parse(jsonStr);
    } catch (error) {
      console.error('数据解析失败:', error);
      return null;
    }
  }

  // 真正的加密存储(需要HTTPS)
  async encryptAndStore(key, data, password) {
    // 使用Web Crypto API进行加密
    // 这里仅展示概念,实际实现更复杂
    console.warn('请使用Web Crypto API实现真正的加密');
  }
}

2. 性能优化

// IndexedDB性能优化技巧
class OptimizedIndexedDB extends IndexedDBManager {
  // 批量操作
  async bulkAdd(storeName, items) {
    return new Promise((resolve, reject) => {
      const transaction = this.db.transaction([storeName], 'readwrite');
      const store = transaction.objectStore(storeName);
      let completed = 0;

      items.forEach((item, index) => {
        const request = store.add(item);
        request.onsuccess = () => {
          completed++;
          if (completed === items.length) {
            resolve();
          }
        };
        request.onerror = () => reject(request.error);
      });
    });
  }

  // 分页查询
  async getPaginated(storeName, page = 1, pageSize = 20) {
    return new Promise((resolve, reject) => {
      const transaction = this.db.transaction([storeName], 'readonly');
      const store = transaction.objectStore(storeName);
      const index = store.index('createdAt'); // 假设有创建时间索引

      const offset = (page - 1) * pageSize;
      const results = [];
      let count = 0;

      const cursorRequest = index.openCursor(null, 'prev'); // 按时间倒序

      cursorRequest.onsuccess = (event) => {
        const cursor = event.target.result;

        if (cursor && count < pageSize) {
          if (results.length >= offset) {
            results.push(cursor.value);
            count++;
          }
          cursor.continue();
        } else {
          resolve({
            page,
            pageSize,
            total: results.length,
            data: results
          });
        }
      };

      cursorRequest.onerror = () => reject(cursorRequest.error);
    });
  }
}

3. 错误处理与回退

// 存储方案降级策略
class StorageManager {
  constructor() {
    this.storageStrategies = [
      this.tryIndexedDB.bind(this),
      this.tryLocalStorage.bind(this),
      this.tryCookie.bind(this)
    ];
  }

  async setItem(key, value) {
    for (const strategy of this.storageStrategies) {
      try {
        await strategy.setItem(key, value);
        return { success: true, method: strategy.name };
      } catch (error) {
        console.warn(`${strategy.name} 失败:`, error);
        continue;
      }
    }
    throw new Error('所有存储方案都失败了');
  }

  async tryIndexedDB() {
    // IndexedDB实现
    const db = new IndexedDBManager('fallbackDB');
    await db.open();
    // ... 具体实现
  }

  tryLocalStorage() {
    // LocalStorage实现
    if (!window.localStorage) throw new Error('LocalStorage不可用');
    localStorage.setItem(key, JSON.stringify(value));
  }

  tryCookie() {
    // Cookie实现
    document.cookie = `${key}=${encodeURIComponent(JSON.stringify(value))}; max-age=31536000`;
  }
}

6. 现代存储方案补充

Cache API(Service Worker缓存)

// 缓存API示例
async function cacheResources() {
  const cacheName = 'my-app-cache-v1';
  const urlsToCache = [
    '/',
    '/styles/main.css',
    '/scripts/app.js',
    '/images/logo.png'
  ];

  try {
    const cache = await caches.open(cacheName);
    await cache.addAll(urlsToCache);
    console.log('资源已缓存');
  } catch (error) {
    console.error('缓存失败:', error);
  }
}

// 从缓存获取
async function getCachedResponse(request) {
  const cache = await caches.open('my-app-cache-v1');
  const cachedResponse = await cache.match(request);

  if (cachedResponse) {
    return cachedResponse;
  }

  // 如果缓存中没有,则请求网络
  const networkResponse = await fetch(request);

  // 可选:将响应添加到缓存
  if (networkResponse.ok) {
    cache.put(request, networkResponse.clone());
  }

  return networkResponse;
}

总结

选择合适的存储方案需要考虑以下因素:

数据大小:小数据用Cookie/Web Storage,大数据用IndexedDB 持久性需求:临时数据用SessionStorage,永久数据用LocalStorage/IndexedDB 结构化程度:简单键值对用Web Storage,复杂数据用IndexedDB 离线需求:IndexedDB + Cache API 安全性要求:敏感数据用HttpOnly Cookie或加密存储

在实际开发中,通常需要组合使用多种存储方案,以平衡性能、容量和功能需求。IndexedDB虽然API较复杂,但对于现代Web应用来说是功能最强大、最灵活的客户端存储方案。

相关推荐