Skip to content

sessionStorage

sessionStorage 是 HTML5 Web Storage API 的另一成员,与 localStorage 使用相同的 API,但数据生命周期完全不同。sessionStorage 的数据仅在当前浏览器标签页(会话)中有效,关闭标签页后数据自动清除。它适用于临时存储表单数据、页面状态、一次性操作数据等场景。

前置知识

阅读本节前,建议先了解:localStorage

基础概念

什么是 sessionStorage

sessionStorage 存储的数据具有"会话级"生命周期,从用户打开页面开始,到关闭标签页或浏览器结束。它与 localStorage 共享完全相同的 API 接口,但在数据持久性和作用范围上存在本质区别。

与 localStorage 对比

特性localStoragesessionStorage
生命周期永久(手动清除前)会话级(关闭标签页即清除)
作用范围同源所有标签页共享仅限当前标签页
存储容量约 5MB约 5MB
数据持久性持久化临时
跨标签页支持不支持
新标签页可访问不可访问
API 接口相同相同

语法与 API

基本操作

javascript
// ========== 存储数据 ==========
sessionStorage.setItem('currentPage', 'dashboard');
sessionStorage.setItem('searchKeyword', 'HTML5 教程');
sessionStorage.setItem('filterStatus', 'active');

// 简写方式
sessionStorage.currentPage = 'dashboard';
sessionStorage['searchKeyword'] = 'HTML5 教程';

// ========== 读取数据 ==========
const page = sessionStorage.getItem('currentPage');     // 'dashboard'
const keyword = sessionStorage.getItem('searchKeyword'); // 'HTML5 教程'

// 不存在的键返回 null
const notExist = sessionStorage.getItem('notExist'); // null

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

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

// ========== 其他操作 ==========
// 获取存储的键数量
console.log('存储了', sessionStorage.length, '条数据');

// 按索引获取键名
const firstKey = sessionStorage.key(0);
console.log('第一个键:', firstKey);

存储复杂数据

javascript
// 存储对象
const formData = {
  step1: { name: '张三', email: 'zhangsan@example.com' },
  step2: { address: '北京市', phone: '13800138000' },
  currentStep: 2
};

sessionStorage.setItem('formData', JSON.stringify(formData));

// 读取对象
const stored = JSON.parse(sessionStorage.getItem('formData'));
console.log(stored.currentStep); // 2

// 存储数组
const selectedItems = ['apple', 'banana', 'orange'];
sessionStorage.setItem('cart', JSON.stringify(selectedItems));

详细说明

标签页隔离机制

sessionStorage 的核心特点是标签页隔离。每个标签页(Tab)拥有独立的 sessionStorage 空间:

javascript
// 在标签页 A 中存储
sessionStorage.setItem('tabId', 'Tab-A');

// 在标签页 B 中(即使打开同一页面)
sessionStorage.getItem('tabId'); // null(无法访问标签页 A 的数据)

// 在标签页 A 中
sessionStorage.getItem('tabId'); // 'Tab-A'

标签页间的关系

场景是否共享 sessionStorage
两个标签页打开同一页面不共享(各自独立)
<a> 标签跳转新页面共享(同一会话)
window.open() 打开新窗口共享(继承会话)
刷新当前页面共享(会话未结束)
新建标签页打开同一 URL不共享(新会话)
iframe(同源)不共享(独立会话)

sessionStorage 的特殊行为

javascript
// 场景一:页面刷新后数据保留
// 在 page1.html 中
sessionStorage.setItem('refreshCount', '0');

// 刷新页面后
let count = Number(sessionStorage.getItem('refreshCount') || '0');
count++;
sessionStorage.setItem('refreshCount', count.toString());
console.log('刷新次数:', count); // 递增

// 场景二:通过 window.open 打开的窗口共享数据
// 主页面
sessionStorage.setItem('shared', '来自主页面的数据');
const newWindow = window.open('sub-page.html');
// 在 sub-page.html 中可以读取 sessionStorage.getItem('shared')

// 场景三:通过链接导航的数据传递
// page1.html
sessionStorage.setItem('fromPage', 'page1');
window.location.href = 'page2.html';
// page2.html 中
console.log(sessionStorage.getItem('fromPage')); // 'page1'

复制标签页的行为

从 Chrome 68+ 开始,复制标签页(Duplicate Tab)会复制原标签页的 sessionStorage

javascript
// 原标签页
sessionStorage.setItem('token', 'abc123');

// 复制标签页
// sessionStorage.getItem('token') === 'abc123'
// 但两个标签页的数据此后各自独立

实战示例

多步骤表单数据暂存

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>sessionStorage 多步骤表单</title>
  <style>
    body { font-family: -apple-system, sans-serif; padding: 40px; background: #f5f7fa; }

    .form-container {
      max-width: 500px;
      margin: 0 auto;
      background: white;
      border-radius: 12px;
      padding: 32px;
      box-shadow: 0 4px 16px rgba(0,0,0,0.1);
    }

    .step { display: none; }
    .step.active { display: block; }

    .step-indicator {
      display: flex;
      justify-content: space-between;
      margin-bottom: 24px;
    }

    .step-dot {
      width: 32px;
      height: 32px;
      border-radius: 50%;
      background: #e0e0e0;
      color: #999;
      display: flex;
      align-items: center;
      justify-content: center;
      font-weight: bold;
    }

    .step-dot.active { background: #1a73e8; color: white; }
    .step-dot.done { background: #34a853; color: white; }

    .form-group {
      margin-bottom: 16px;
    }

    .form-group label {
      display: block;
      margin-bottom: 6px;
      font-weight: 600;
      color: #333;
    }

    .form-group input {
      width: 100%;
      padding: 10px 12px;
      border: 2px solid #e0e0e0;
      border-radius: 8px;
      font-size: 14px;
      transition: border-color 0.2s;
    }

    .form-group input:focus {
      outline: none;
      border-color: #1a73e8;
    }

    .buttons {
      display: flex;
      justify-content: space-between;
      margin-top: 24px;
    }

    .btn {
      padding: 10px 24px;
      border: none;
      border-radius: 8px;
      cursor: pointer;
      font-size: 14px;
      font-weight: 600;
    }

    .btn-primary { background: #1a73e8; color: white; }
    .btn-secondary { background: #e0e0e0; color: #333; }

    .summary {
      background: #f8f9fa;
      padding: 16px;
      border-radius: 8px;
      line-height: 1.8;
    }

    .restore-hint {
      margin-top: 12px;
      padding: 8px 12px;
      background: #fff3cd;
      border-radius: 6px;
      font-size: 13px;
      color: #856404;
    }
  </style>
</head>
<body>
  <div class="form-container">
    <h2>用户注册(三步表单)</h2>

    <div class="step-indicator">
      <div class="step-dot active" id="dot1">1</div>
      <div class="step-dot" id="dot2">2</div>
      <div class="step-dot" id="dot3">3</div>
    </div>

    <!-- 步骤 1:基本信息 -->
    <div class="step active" id="step1">
      <div class="form-group">
        <label>用户名</label>
        <input type="text" id="username" placeholder="请输入用户名">
      </div>
      <div class="form-group">
        <label>邮箱</label>
        <input type="email" id="email" placeholder="请输入邮箱">
      </div>
      <div class="buttons">
        <div></div>
        <button class="btn btn-primary" onclick="nextStep(1)">下一步</button>
      </div>
    </div>

    <!-- 步骤 2:详细信息 -->
    <div class="step" id="step2">
      <div class="form-group">
        <label>手机号</label>
        <input type="tel" id="phone" placeholder="请输入手机号">
      </div>
      <div class="form-group">
        <label>地址</label>
        <input type="text" id="address" placeholder="请输入地址">
      </div>
      <div class="buttons">
        <button class="btn btn-secondary" onclick="prevStep(2)">上一步</button>
        <button class="btn btn-primary" onclick="nextStep(2)">下一步</button>
      </div>
    </div>

    <!-- 步骤 3:确认提交 -->
    <div class="step" id="step3">
      <div class="summary" id="summary"></div>
      <div class="restore-hint" id="restoreHint" style="display:none;">
        提示:已从 sessionStorage 恢复之前填写的数据
      </div>
      <div class="buttons">
        <button class="btn btn-secondary" onclick="prevStep(3)">上一步</button>
        <button class="btn btn-primary" onclick="submitForm()">提交</button>
      </div>
    </div>
  </div>

  <script>
    const STORAGE_KEY = 'multiStepForm';

    // 保存当前步骤数据到 sessionStorage
    function saveStepData(step) {
      const data = getFormData();
      data.currentStep = step;
      sessionStorage.setItem(STORAGE_KEY, JSON.stringify(data));
    }

    // 从 sessionStorage 读取数据
    function getFormData() {
      try {
        return JSON.parse(sessionStorage.getItem(STORAGE_KEY)) || {};
      } catch {
        return {};
      }
    }

    // 恢复表单数据
    function restoreFormData() {
      const data = getFormData();
      if (data.currentStep) {
        document.getElementById('username').value = data.username || '';
        document.getElementById('email').value = data.email || '';
        document.getElementById('phone').value = data.phone || '';
        document.getElementById('address').value = data.address || '';
        goToStep(data.currentStep);

        if (data.username || data.email) {
          document.getElementById('restoreHint').style.display = 'block';
        }
      }
    }

    // 切换步骤
    function goToStep(step) {
      document.querySelectorAll('.step').forEach(s => s.classList.remove('active'));
      document.getElementById(`step${step}`).classList.add('active');

      document.querySelectorAll('.step-dot').forEach((dot, i) => {
        dot.className = 'step-dot';
        if (i + 1 < step) dot.classList.add('done');
        if (i + 1 === step) dot.classList.add('active');
      });
    }

    // 下一步
    function nextStep(current) {
      saveStepData(current + 1);
      goToStep(current + 1);

      if (current + 1 === 3) {
        showSummary();
      }
    }

    // 上一步
    function prevStep(current) {
      saveStepData(current - 1);
      goToStep(current - 1);
    }

    // 显示摘要
    function showSummary() {
      const data = getFormData();
      document.getElementById('summary').innerHTML = `
        <p><strong>用户名:</strong> ${data.username || '未填写'}</p>
        <p><strong>邮箱:</strong> ${data.email || '未填写'}</p>
        <p><strong>手机号:</strong> ${data.phone || '未填写'}</p>
        <p><strong>地址:</strong> ${data.address || '未填写'}</p>
      `;
    }

    // 提交表单
    function submitForm() {
      const data = getFormData();
      console.log('提交数据:', data);

      // 提交后清除 sessionStorage
      sessionStorage.removeItem(STORAGE_KEY);
      alert('注册成功!sessionStorage 数据已清除。');
    }

    // 页面加载时恢复数据(刷新页面后数据保留)
    restoreFormData();
  </script>
</body>
</html>

注意事项

sessionStorage 与页面跳转

javascript
// 跨页面数据传递方案对比

// 方案一:sessionStorage(同一标签页内跳转有效)
sessionStorage.setItem('data', 'hello');
window.location.href = '/other-page.html';
// 在 other-page.html 中可以读取

// 方案二:URL 参数(跨标签页也有效)
window.location.href = '/other-page.html?data=hello';

// 方案三:postMessage(跨域、跨标签页有效)
otherWindow.postMessage({ data: 'hello' }, '*');

// 方案四:BroadcastChannel(跨标签页、同源有效)
const channel = new BroadcastChannel('my-channel');
channel.postMessage('hello');

浏览器兼容性

浏览器支持特殊行为
Chrome完全支持复制标签页会复制数据
Firefox完全支持复制标签页不复制数据
Safari完全支持隐私模式下容量受限
IE8+支持IE7 及以下不支持
移动端支持后台标签页可能被系统回收

最佳实践

  1. 适合 sessionStorage 的场景:表单暂存、一次性向导、页面状态、临时过滤条件
  2. 不适合 sessionStorage 的场景:需要跨标签页的数据、长期缓存、大量数据存储
  3. 刷新恢复:利用页面刷新时 sessionStorage 不清除的特性,实现表单数据恢复
  4. 及时清理:操作完成后主动 removeItemclear,避免残留数据

下一节

继续学习:Storage API 详解

参考链接