7.1 Web Storage(localStorage/sessionStorage)

7.1 Web Storage (localStorage/sessionStorage)

Web Storage API 是现代浏览器提供的客户端存储解决方案,包含两种主要机制:localStoragesessionStorage。这两种存储方式为前端开发提供了简单、高效的键值对存储能力,克服了传统cookie的诸多限制。

核心特性对比

特性 localStorage sessionStorage
生命周期 永久存储,除非手动清除 会话级别存储,标签页关闭时清除
作用域 同源下所有标签页共享 仅限当前标签页
存储限额 通常5MB-10MB(各浏览器不同) 同localStorage
数据格式 仅支持字符串 同localStorage
访问方式 同步操作 同localStorage
适用场景 长期偏好设置、离线数据 临时表单数据、页面间传参

基本操作指南

1. 存储数据

// localStorage 示例
localStorage.setItem('theme', 'dark');          // 存储字符串
localStorage.setItem('user', JSON.stringify({   // 存储对象需要序列化
  id: 123,
  name: 'John Doe'
}));

// sessionStorage 示例
sessionStorage.setItem('formDraft', JSON.stringify({
  username: '',
  email: '',
  preferences: {}
}));

2. 读取数据

// 读取简单值
const theme = localStorage.getItem('theme'); // "dark"

// 读取对象
const userStr = localStorage.getItem('user');
const user = userStr ? JSON.parse(userStr) : null;

// 检查sessionStorage中的临时数据
const draft = sessionStorage.getItem('formDraft');
if (draft) {
  formState = JSON.parse(draft);
}

3. 删除数据

// 移除单个项
localStorage.removeItem('theme');

// 清空所有存储
sessionStorage.clear();

// 注意:clear()会移除同源下的所有数据,慎用

高级使用技巧

1. 存储事件监听

当同源下的其他标签页修改存储时,可以监听变化:

window.addEventListener('storage', (event) => {
  console.log(`键 ${event.key} 发生变化`);
  console.log(`旧值: ${event.oldValue}`);
  console.log(`新值: ${event.newValue}`);
  console.log(`来源: ${event.url}`);
  
  // 主题切换示例
  if (event.key === 'theme') {
    document.body.className = event.newValue;
  }
});

2. 容量检测与处理

function testStorageQuota() {
  const testKey = 'quotaTest';
  const testData = new Array(1024 * 1024).join('a'); // 1MB数据
  
  try {
    localStorage.setItem(testKey, testData);
    localStorage.removeItem(testKey);
    return true;
  } catch (e) {
    if (e.name === 'QuotaExceededError') {
      console.warn('存储空间不足');
      // 执行清理逻辑
      clearOldCache();
      return false;
    }
  }
}

function clearOldCache() {
  // 按时间戳清理旧数据
  const now = Date.now();
  for (let i = 0; i < localStorage.length; i++) {
    const key = localStorage.key(i);
    if (key.startsWith('cache_')) {
      const data = JSON.parse(localStorage.getItem(key));
      if (now - data.timestamp > 30 * 24 * 60 * 60 * 1000) { // 30天前
        localStorage.removeItem(key);
      }
    }
  }
}

3. 命名空间管理

// 创建命名空间类
class AppStorage {
  constructor(namespace) {
    this.ns = namespace + ':';
  }
  
  set(key, value) {
    localStorage.setItem(this.ns + key, JSON.stringify(value));
  }
  
  get(key) {
    const data = localStorage.getItem(this.ns + key);
    return data ? JSON.parse(data) : null;
  }
  
  getAll() {
    const items = {};
    for (let i = 0; i < localStorage.length; i++) {
      const key = localStorage.key(i);
      if (key.startsWith(this.ns)) {
        const realKey = key.slice(this.ns.length);
        items[realKey] = this.get(realKey);
      }
    }
    return items;
  }
}

// 使用示例
const userStorage = new AppStorage('user_prefs');
userStorage.set('theme', { mode: 'dark', accent: '#ff5722' });
const prefs = userStorage.getAll();

实际应用场景

1. 用户偏好设置

// 保存主题偏好
function saveThemePreference(theme) {
  localStorage.setItem('app:theme', JSON.stringify({
    mode: theme,
    lastUpdated: new Date().toISOString()
  }));
}

// 初始化时应用主题
function applySavedTheme() {
  const saved = localStorage.getItem('app:theme');
  if (saved) {
    const { mode } = JSON.parse(saved);
    document.documentElement.setAttribute('data-theme', mode);
  }
}

2. 表单自动保存

// 监听表单变化
const form = document.getElementById('settings-form');
form.addEventListener('input', debounce(() => {
  const formData = new FormData(form);
  const data = Object.fromEntries(formData);
  sessionStorage.setItem('formAutosave', JSON.stringify(data));
}, 500));

// 页面加载时恢复
window.addEventListener('DOMContentLoaded', () => {
  const saved = sessionStorage.getItem('formAutosave');
  if (saved) {
    const data = JSON.parse(saved);
    for (const [name, value] of Object.entries(data)) {
      if (form.elements[name]) {
        form.elements[name].value = value;
      }
    }
  }
});

3. 购物车临时存储

class CartService {
  static KEY = 'cart_v2';
  
  static getItems() {
    const cart = sessionStorage.getItem(this.KEY);
    return cart ? JSON.parse(cart) : [];
  }
  
  static addItem(product) {
    const items = this.getItems();
    const existing = items.find(item => item.id === product.id);
    
    if (existing) {
      existing.quantity += 1;
    } else {
      items.push({ ...product, quantity: 1 });
    }
    
    sessionStorage.setItem(this.KEY, JSON.stringify(items));
    this.dispatchUpdate();
  }
  
  static dispatchUpdate() {
    window.dispatchEvent(new CustomEvent('cartUpdated', {
      detail: this.getItems()
    }));
  }
}

// 使用示例
CartService.addItem({ id: 101, name: 'Web开发指南', price: 99 });
window.addEventListener('cartUpdated', (e) => {
  updateCartUI(e.detail);
});

安全注意事项

  1. XSS攻击风险

    • 永远不要存储敏感信息(如密码、令牌)
    • 对存储的数据进行消毒处理
  2. 数据验证

    function getSafeConfig() {
      try {
        const config = JSON.parse(localStorage.getItem('appConfig'));
        if (config && typeof config === 'object') {
          return sanitize(config); // 实现数据消毒逻辑
        }
      } catch (e) {
        console.error('配置解析失败', e);
      }
      return getDefaultConfig();
    }
    
  3. 隐私合规

    • GDPR等法规要求
    • 提供清除数据的选项
    <button id="clearData">清除我的本地数据</button>
    <script>
      document.getElementById('clearData').addEventListener('click', () => {
        if (confirm('确定要清除所有本地存储数据吗?')) {
          localStorage.clear();
          sessionStorage.clear();
        }
      });
    </script>
    

性能优化策略

  1. 批量操作

    function batchSaveItems(items) {
      const batch = {};
      items.forEach(item => {
        batch[`item_${item.id}`] = item;
      });
      localStorage.setItem('itemBatch', JSON.stringify(batch));
    }
    
  2. 数据压缩

    function compressSave(key, data) {
      const str = JSON.stringify(data);
      // 简单base64压缩(实际项目可使用lz-string等库)
      localStorage.setItem(key, btoa(unescape(encodeURIComponent(str))));
    }
    
    function compressLoad(key) {
      const compressed = localStorage.getItem(key);
      if (!compressed) return null;
      try {
        return JSON.parse(decodeURIComponent(escape(atob(compressed))));
      } catch (e) {
        return null;
      }
    }
    
  3. 过期策略

    function setWithExpiry(key, value, expiryDays) {
      const item = {
        value: value,
        expiry: Date.now() + (expiryDays * 24 * 60 * 60 * 1000)
      };
      localStorage.setItem(key, JSON.stringify(item));
    }
    
    function getWithExpiry(key) {
      const itemStr = localStorage.getItem(key);
      if (!itemStr) return null;
      
      const item = JSON.parse(itemStr);
      if (Date.now() > item.expiry) {
        localStorage.removeItem(key);
        return null;
      }
      return item.value;
    }
    

现代浏览器的替代方案

虽然Web Storage简单易用,但对于更复杂的场景,可以考虑:

  1. IndexedDB

    • 更大的存储空间
    • 支持事务和索引查询
    • 适合结构化数据
  2. Cache API

    • 专为网络资源设计
    • 配合Service Worker使用
    • 实现离线应用
  3. Web SQL(已废弃):

    • 虽然被大多数浏览器支持
    • 但已被W3C废弃,不推荐新项目使用

Web Storage作为浏览器存储的基石,在适当的场景下仍然是简单高效的选择。理解其特性和限制,能够帮助开发者在各种存储方案中做出合理决策。

#前端开发 分享于 2025-05-20

上一篇:6.5 WebGL 简介 下一篇:7.2 IndexedDB 基础
【 内容由 AI 共享,不代表本站观点,请谨慎参考 】