JavaScript本地存储

什么是本地存储?

本地存储是指在浏览器中存储数据的能力,这些数据保存在用户的设备上,不需要服务器参与。JavaScript提供了多种本地存储方式,其中最常用的是LocalStorage和SessionStorage。

本地存储的主要优点包括:

  • 数据持久化:数据可以在浏览器关闭后仍然保留
  • 减少网络请求:可以将频繁使用的数据存储在本地,减少服务器请求
  • 提高性能:本地读取数据比网络请求更快
  • 离线支持:即使在离线状态下,也可以访问存储的数据

LocalStorage

LocalStorage是一种持久化的本地存储方式,数据存储在浏览器中,不会随着浏览器窗口关闭而删除,除非手动清除。

LocalStorage的特点

  • 数据以键值对的形式存储
  • 数据大小限制:通常为5MB左右
  • 同源策略:只有同一个域名下的页面可以访问存储的数据
  • 数据类型:只能存储字符串类型,其他类型需要手动转换
  • 持久化存储:数据不会过期,除非手动删除

LocalStorage的基本操作

设置数据

使用setItem()方法设置数据,参数为键名和键值。

// 存储字符串
localStorage.setItem('username', 'John');

// 存储数字
localStorage.setItem('age', '30');

// 存储对象(需要转换为字符串)
const user = { name: 'John', age: 30 };
localStorage.setItem('user', JSON.stringify(user));

// 存储数组(需要转换为字符串)
const hobbies = ['reading', 'coding', 'traveling'];
localStorage.setItem('hobbies', JSON.stringify(hobbies));

获取数据

使用getItem()方法获取数据,参数为键名。

// 获取字符串
const username = localStorage.getItem('username');
console.log(username); // John

// 获取数字(需要转换为数字类型)
const age = parseInt(localStorage.getItem('age'));
console.log(age); // 30

// 获取对象(需要转换为对象类型)
const user = JSON.parse(localStorage.getItem('user'));
console.log(user.name); // John

// 获取数组(需要转换为数组类型)
const hobbies = JSON.parse(localStorage.getItem('hobbies'));
console.log(hobbies[0]); // reading

删除数据

使用removeItem()方法删除指定键名的数据。

// 删除单个数据
localStorage.removeItem('age');

清空所有数据

使用clear()方法清空所有存储的数据。

// 清空所有数据
localStorage.clear();

获取所有键名

使用key()方法获取指定索引的键名,结合length属性可以遍历所有键名。

// 遍历所有键值对
for (let i = 0; i < localStorage.length; i++) {
  const key = localStorage.key(i);
  const value = localStorage.getItem(key);
  console.log(`${key}: ${value}`);
}

SessionStorage

SessionStorage是一种会话级别的本地存储方式,数据存储在浏览器中,当浏览器窗口关闭时,数据会被自动删除。

SessionStorage的特点

  • 数据以键值对的形式存储
  • 数据大小限制:通常为5MB左右
  • 同源策略:只有同一个域名下的页面可以访问存储的数据
  • 数据类型:只能存储字符串类型,其他类型需要手动转换
  • 会话级存储:数据在浏览器窗口关闭时自动删除
  • 标签页隔离:不同标签页之间的数据不共享

SessionStorage的基本操作

SessionStorage的API与LocalStorage完全相同,只是存储的生命周期不同。

// 设置数据
sessionStorage.setItem('sessionId', '12345');

// 获取数据
const sessionId = sessionStorage.getItem('sessionId');
console.log(sessionId); // 12345

// 删除数据
sessionStorage.removeItem('sessionId');

// 清空所有数据
sessionStorage.clear();

LocalStorage与SessionStorage的区别

特性 LocalStorage SessionStorage
存储生命周期 持久化,除非手动删除 会话级,窗口关闭后删除
跨标签页共享
数据大小 约5MB 约5MB
API 相同 相同
适用场景 长期存储的数据,如用户偏好设置 临时数据,如表单数据、会话ID

本地存储的使用场景

1. 用户偏好设置

// 保存用户主题偏好
function saveThemePreference(theme) {
  localStorage.setItem('theme', theme);
}

// 获取用户主题偏好
function getThemePreference() {
  return localStorage.getItem('theme') || 'light';
}

// 应用主题偏好
const theme = getThemePreference();
document.body.className = `theme-${theme}`;

2. 表单数据缓存

// 保存表单数据
document.addEventListener('input', (event) => {
  const { name, value } = event.target;
  const formData = JSON.parse(localStorage.getItem('formData') || '{}');
  formData[name] = value;
  localStorage.setItem('formData', JSON.stringify(formData));
});

// 恢复表单数据
window.addEventListener('DOMContentLoaded', () => {
  const formData = JSON.parse(localStorage.getItem('formData') || '{}');
  Object.entries(formData).forEach(([name, value]) => {
    const input = document.querySelector(`[name="${name}"]`);
    if (input) {
      input.value = value;
    }
  });
});

3. 缓存API响应

// 缓存API响应
async function fetchData(url, cacheKey) {
  // 检查缓存是否存在
  const cachedData = localStorage.getItem(cacheKey);
  if (cachedData) {
    console.log('使用缓存数据');
    return JSON.parse(cachedData);
  }
  
  // 发送网络请求
  console.log('发送网络请求');
  const response = await fetch(url);
  const data = await response.json();
  
  // 缓存数据
  localStorage.setItem(cacheKey, JSON.stringify(data));
  return data;
}

// 使用示例
fetchData('https://api.example.com/data', 'api-data')
  .then(data => {
    console.log(data);
  });

4. 购物车数据

// 添加商品到购物车
function addToCart(product) {
  const cart = JSON.parse(localStorage.getItem('cart') || '[]');
  cart.push(product);
  localStorage.setItem('cart', JSON.stringify(cart));
}

// 获取购物车数据
function getCart() {
  return JSON.parse(localStorage.getItem('cart') || '[]');
}

// 清空购物车
function clearCart() {
  localStorage.removeItem('cart');
}

本地存储的限制和注意事项

1. 数据类型限制

本地存储只能存储字符串类型的数据,其他类型(如对象、数组、数字)需要手动转换。

// 错误:直接存储对象
localStorage.setItem('user', { name: 'John' }); // 存储结果为 "[object Object]"

// 正确:先转换为字符串
localStorage.setItem('user', JSON.stringify({ name: 'John' }));

2. 数据大小限制

本地存储的大小通常限制为5MB左右,超过这个限制会抛出错误。

// 检查存储大小
function checkStorageSize() {
  let totalSize = 0;
  for (let key in localStorage) {
    if (localStorage.hasOwnProperty(key)) {
      totalSize += localStorage[key].length;
    }
  }
  console.log(`当前存储大小:${totalSize} 字节`);
  return totalSize;
}

3. 同源策略

本地存储遵循同源策略,只有同一个域名、协议和端口下的页面可以访问存储的数据。

4. 安全性问题

本地存储中的数据是明文存储的,不适合存储敏感信息,如密码、信用卡信息等。

// 不推荐:存储敏感信息
localStorage.setItem('password', '123456');
localStorage.setItem('creditCard', '1234-5678-9012-3456');

5. 性能问题

频繁读写本地存储会影响性能,尤其是在循环中。

// 不推荐:频繁写入本地存储
for (let i = 0; i < 1000; i++) {
  localStorage.setItem(`item-${i}`, i);
}

// 推荐:批量处理,减少写入次数
const items = {};
for (let i = 0; i < 1000; i++) {
  items[`item-${i}`] = i;
}
localStorage.setItem('items', JSON.stringify(items));

本地存储的最佳实践

  1. 只存储必要的数据:避免存储大量数据,只存储频繁使用的数据
  2. 定期清理过期数据:对于有过期时间的数据,定期检查并清理
  3. 使用合适的存储方式:根据数据的生命周期选择LocalStorage或SessionStorage
  4. 加密敏感数据:如果必须存储敏感数据,使用加密算法加密后再存储
  5. 错误处理:添加错误处理代码,防止存储失败导致应用崩溃
  6. 数据验证:从本地存储读取数据后,进行验证,确保数据格式正确
  7. 批量操作:减少读写次数,提高性能
  8. 使用命名空间:为不同功能的数据添加前缀,避免键名冲突

本地存储的替代方案

1. IndexedDB

IndexedDB是一种更强大的本地存储方案,适合存储大量结构化数据。

2. Cookies

Cookies也可以用于本地存储,但它们有以下限制:

  • 数据大小限制:约4KB
  • 每次请求都会发送到服务器
  • 可以设置过期时间

3. Cache API

Cache API主要用于缓存HTTP请求和响应,是Service Worker的一部分,用于实现离线功能。

示例:完整的本地存储应用

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>本地存储示例</title>
  <style>
    .dark-theme {
      background-color: #333;
      color: #fff;
    }
    .light-theme {
      background-color: #fff;
      color: #333;
    }
  </style>
</head>
<body class="light-theme">
  <h1>本地存储示例</h1>
  
  <h2>主题切换</h2>
  <button id="light-theme-btn">浅色主题</button>
  <button id="dark-theme-btn">深色主题</button>
  
  <h2>表单数据缓存</h2>
  <form id="example-form">
    <div>
      <label for="name">姓名:</label>
      <input type="text" id="name" name="name">
    </div>
    <div>
      <label for="email">邮箱:</label>
      <input type="email" id="email" name="email">
    </div>
    <div>
      <label for="message">留言:</label>
      <textarea id="message" name="message" rows="3"></textarea>
    </div>
    <button type="submit">提交</button>
    <button type="button" id="clear-form">清空表单</button>
  </form>
  
  <script>
    // 主题切换功能
    const lightThemeBtn = document.getElementById('light-theme-btn');
    const darkThemeBtn = document.getElementById('dark-theme-btn');
    const body = document.body;
    
    // 加载主题偏好
    const savedTheme = localStorage.getItem('theme') || 'light';
    body.className = `${savedTheme}-theme`;
    
    // 切换到浅色主题
    lightThemeBtn.addEventListener('click', () => {
      body.className = 'light-theme';
      localStorage.setItem('theme', 'light');
    });
    
    // 切换到深色主题
    darkThemeBtn.addEventListener('click', () => {
      body.className = 'dark-theme';
      localStorage.setItem('theme', 'dark');
    });
    
    // 表单数据缓存功能
    const form = document.getElementById('example-form');
    const clearFormBtn = document.getElementById('clear-form');
    
    // 加载表单数据
    const savedFormData = JSON.parse(localStorage.getItem('formData') || '{}');
    Object.entries(savedFormData).forEach(([name, value]) => {
      const input = form.querySelector(`[name="${name}"]`);
      if (input) {
        input.value = value;
      }
    });
    
    // 监听表单输入,保存数据
    form.addEventListener('input', (event) => {
      const { name, value } = event.target;
      const formData = JSON.parse(localStorage.getItem('formData') || '{}');
      formData[name] = value;
      localStorage.setItem('formData', JSON.stringify(formData));
    });
    
    // 提交表单,清除缓存
    form.addEventListener('submit', (event) => {
      event.preventDefault();
      localStorage.removeItem('formData');
      alert('表单提交成功!');
      form.reset();
    });
    
    // 清空表单和缓存
    clearFormBtn.addEventListener('click', () => {
      localStorage.removeItem('formData');
      form.reset();
    });
  </script>
</body>
</html>

总结

本地存储是JavaScript中非常有用的功能,允许在浏览器中存储数据,提供了持久化存储和会话级存储两种方式。LocalStorage适用于需要长期保存的数据,而SessionStorage适用于临时数据。

在使用本地存储时,需要注意数据类型限制、大小限制、同源策略和安全性问题。通过遵循最佳实践,可以充分利用本地存储的优势,同时避免潜在的问题。

本地存储广泛应用于用户偏好设置、表单数据缓存、API响应缓存、购物车数据等场景,是现代Web应用中不可或缺的一部分。

练习

  1. 实现一个简单的待办事项列表,使用LocalStorage保存数据,包括添加、删除和标记完成功能。

  2. 实现一个主题切换功能,支持浅色主题和深色主题,使用LocalStorage保存用户偏好。

  3. 实现一个表单数据自动保存功能,当用户输入表单数据时,自动保存到LocalStorage,页面刷新后可以恢复。

  4. 实现一个简单的API响应缓存功能,将API响应缓存到LocalStorage,下次请求时如果缓存存在且未过期,则直接使用缓存数据。

  5. 实现一个购物车功能,使用LocalStorage保存购物车数据,包括添加商品、删除商品、修改商品数量和清空购物车功能。

« 上一篇 JavaScript Web API 下一篇 » JavaScript Cookies