欢迎光临殡葬网
详情描述
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=/`;
}

特点与限制

  • 大小限制:每个域名4KB左右
  • 自动发送:每次HTTP请求都会自动携带
  • 存储时间:可设置过期时间
  • 安全问题:需注意HttpOnly、Secure、SameSite属性

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应用来说是功能最强大、最灵活的客户端存储方案。

相关帖子
节假日共享单车价格涨到两块五,和打车公交比还有没有优势?
节假日共享单车价格涨到两块五,和打车公交比还有没有优势?
面对生物识别技术的广泛应用,公众的接受度和心理感受发生了哪些变化?
面对生物识别技术的广泛应用,公众的接受度和心理感受发生了哪些变化?
紧急停车按钮被启动后,会对整个地铁线路的运行造成多大影响?
紧急停车按钮被启动后,会对整个地铁线路的运行造成多大影响?
慢性肾炎能引起尿毒症吗
慢性肾炎能引起尿毒症吗
在海南环岛公路旅行,如何利用公共交通工具和分段骑行来完成环岛体验?
在海南环岛公路旅行,如何利用公共交通工具和分段骑行来完成环岛体验?
在食用当季蔬果时,有哪些有效的家庭处理方法可以减少农残疑虑?
在食用当季蔬果时,有哪些有效的家庭处理方法可以减少农残疑虑?
因手机信号或系统问题导致无法按时核销,会被误判为爽约吗?
因手机信号或系统问题导致无法按时核销,会被误判为爽约吗?
2026年中考综合素质评价越来越重要,小学阶段该怎么帮孩子攒素材?
2026年中考综合素质评价越来越重要,小学阶段该怎么帮孩子攒素材?
当有意外收入时,将其转为储蓄而非消费的心理技巧有哪些?
当有意外收入时,将其转为储蓄而非消费的心理技巧有哪些?
对于刚参加工作不久的年轻人,租房提取住房公积金有哪些便利条件与渠道?
对于刚参加工作不久的年轻人,租房提取住房公积金有哪些便利条件与渠道?
近年有哪些新兴的行业或赛道获得了更多的创业政策倾斜?
近年有哪些新兴的行业或赛道获得了更多的创业政策倾斜?
过桥垫资费用如何核算?结合计费规则看懂单日收费与总成本计算
过桥垫资费用如何核算?结合计费规则看懂单日收费与总成本计算
房产抵押贷款相关法规解读,学法懂法保障抵押借贷行为合法合规
房产抵押贷款相关法规解读,学法懂法保障抵押借贷行为合法合规
台风来临前窗外突然变得安静,真的是风暴已经过去了吗?
台风来临前窗外突然变得安静,真的是风暴已经过去了吗?
押车贷款办理流程详解,签订借据时借款用途怎么填写?
押车贷款办理流程详解,签订借据时借款用途怎么填写?
大暑三候
大暑三候"腐草为萤、土润溽暑、大雨时行"该怎么理解?
解读相关法律法规对“最终解释权归本店所有”这类单方声明的具体规定
解读相关法律法规对“最终解释权归本店所有”这类单方声明的具体规定
2026年有没有更智能的个人收款码账单管理与分析工具?
2026年有没有更智能的个人收款码账单管理与分析工具?
房产证红本抵押贷款办理流程全解析,从申请到放款只需七步
房产证红本抵押贷款办理流程全解析,从申请到放款只需七步
头伏饺子二伏面三伏烙饼摊鸡蛋,这套吃法背后是哪里的民俗习惯?
头伏饺子二伏面三伏烙饼摊鸡蛋,这套吃法背后是哪里的民俗习惯?