pin_drop当前位置:知识文库 ❯ 图文

HTML5 API:HTML5 SessionStorage - 完整教程与代码示例

一、教程简介

SessionStorage 是 Web Storage API 的一部分,提供了一种会话级别的键值对存储机制。与 localStorage 的持久化存储不同,sessionStorage 中存储的数据仅在当前浏览器标签页(或窗口)的生命周期内有效,当标签页关闭后数据即被清除。这种特性使 sessionStorage 特别适合存储临时性、会话性的数据,如多步骤表单的中间状态、一次性令牌、页面间的临时参数传递等。


二、核心概念

会话生命周期

代码示例

标签页打开 → sessionStorage 创建
    │
    ├── 页面刷新 → 数据保留
    ├── 页面导航 → 数据保留(同源)
    ├── 恢复页面 → 数据保留(部分浏览器)
    │
    └── 标签页关闭 → 数据清除

标签页隔离

sessionStorage 最重要的特性是标签页隔离

  • 每个标签页拥有独立的 sessionStorage 实例

  • 即使是同一 URL 打开的多个标签页,也各自拥有独立的数据

  • 标签页之间无法共享 sessionStorage 数据

  • 通过 window.open<a target="_blank"> 打开的新标签页会复制一份当前标签页的 sessionStorage 快照

与 localStorage 的区别

特性 sessionStorage localStorage
生命周期 标签页关闭即清除 永久存储,需手动清除
作用域 当前标签页 同源所有标签页共享
存储容量 约 5-10MB 约 5-10MB
storage 事件 不触发(自身标签页) 跨标签页触发
典型场景 表单临时数据、会话令牌 用户偏好、持久缓存

API 接口

代码示例

// 设置数据
sessionStorage.setItem(key, value);

// 读取数据
const value = sessionStorage.getItem(key);

// 删除指定键
sessionStorage.removeItem(key);

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

// 按索引获取键名
const key = sessionStorage.key(index);

// 获取存储条目数量
const length = sessionStorage.length;

三、语法与用法

基本操作

代码示例

// 存储
sessionStorage.setItem('tempData', '临时数据');
sessionStorage.setItem('step', '2');

// 读取
const data = sessionStorage.getItem('tempData'); // '临时数据'
const step = sessionStorage.getItem('step');     // '2'(字符串)

// 删除
sessionStorage.removeItem('tempData');

// 清空
sessionStorage.clear();

// 遍历
for (let i = 0; i < sessionStorage.length; i++) {
    console.log(sessionStorage.key(i), sessionStorage.getItem(sessionStorage.key(i)));
}

属性访问方式

代码示例

// 等价于 setItem / getItem
sessionStorage.username = '张三';
console.log(sessionStorage.username); // '张三'

// 注意:如果键名与 Storage 原型方法同名,属性访问会出问题
sessionStorage.setItem = 'test'; // 不要这样做!
// 应该始终使用方法调用

存储复杂数据

代码示例

// 存储对象
const formData = {
    name: '张三',
    email: 'zhangsan@example.com',
    preferences: { theme: 'dark', language: 'zh-CN' }
};
sessionStorage.setItem('formData', JSON.stringify(formData));

// 读取对象
const saved = JSON.parse(sessionStorage.getItem('formData'));

四、代码示例

示例一:多步骤表单数据保存

代码示例

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>SessionStorage - 多步骤表单</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body {
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh; display: flex; justify-content: center;
            align-items: center; padding: 20px;
        }
        .container {
            background: #fff; border-radius: 16px; padding: 40px;
            max-width: 520px; width: 100%;
            box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
        }
        .progress-bar {
            display: flex; justify-content: space-between;
            margin-bottom: 32px; position: relative;
        }
        .step {
            width: 32px; height: 32px; border-radius: 50%;
            background: #e0e0e0; color: #999;
            display: flex; align-items: center; justify-content: center;
            font-size: 14px; font-weight: 700;
            transition: all 0.3s;
        }
        .step.active { background: #667eea; color: #fff; }
        .step.done { background: #00b894; color: #fff; }
        h2 { font-size: 20px; color: #333; margin-bottom: 24px; }
        .form-group { margin-bottom: 18px; }
        label {
            display: block; font-size: 13px; color: #666;
            margin-bottom: 6px; font-weight: 500;
        }
        input, select {
            width: 100%; padding: 10px 14px;
            border: 1px solid #ddd; border-radius: 8px;
            font-size: 14px; outline: none;
        }
        input:focus, select:focus { border-color: #667eea; }
        .btn-group { display: flex; gap: 12px; margin-top: 24px; }
        button {
            flex: 1; padding: 12px; border: none;
            border-radius: 8px; font-size: 15px;
            font-weight: 600; cursor: pointer;
        }
        .btn-next { background: #667eea; color: #fff; }
        .btn-prev { background: #f0f0f0; color: #555; }
        .btn-submit { background: #00b894; color: #fff; }
        .step-content { display: none; }
        .step-content.active { display: block; }
    </style>
</head>
<body>
    <div class="container">
        <div class="progress-bar">
            <div class="step active" id="step1">1</div>
            <div class="step" id="step2">2</div>
            <div class="step" id="step3">3</div>
        </div>
        <div class="step-content active" id="stepContent1">
            <h2>基本信息</h2>
            <div class="form-group">
                <label>姓名</label>
                <input type="text" id="name" placeholder="请输入姓名">
            </div>
            <div class="form-group">
                <label>邮箱</label>
                <input type="email" id="email" placeholder="请输入邮箱">
            </div>
            <div class="btn-group">
                <button class="btn-next" onclick="nextStep(1)">下一步</button>
            </div>
        </div>
        <div class="step-content" id="stepContent2">
            <h2>偏好设置</h2>
            <div class="form-group">
                <label>主题</label>
                <select id="theme">
                    <option value="light">浅色模式</option>
                    <option value="dark">深色模式</option>
                </select>
            </div>
            <div class="btn-group">
                <button class="btn-prev" onclick="prevStep(2)">上一步</button>
                <button class="btn-next" onclick="nextStep(2)">下一步</button>
            </div>
        </div>
        <div class="step-content" id="stepContent3">
            <h2>确认信息</h2>
            <div class="btn-group">
                <button class="btn-prev" onclick="prevStep(3)">上一步</button>
                <button class="btn-submit" onclick="submitForm()">提交</button>
            </div>
        </div>
    </div>
    <script>
        const STORAGE_KEY = 'multiStepForm';
        function loadFormData() {
            const saved = sessionStorage.getItem(STORAGE_KEY);
            if (saved) {
                try {
                    const data = JSON.parse(saved);
                    if (data.name) document.getElementById('name').value = data.name;
                    if (data.email) document.getElementById('email').value = data.email;
                    if (data.theme) document.getElementById('theme').value = data.theme;
                    if (data.currentStep) showStep(data.currentStep);
                } catch (e) { console.error('数据恢复失败:', e); }
            }
        }
        function saveStepData(step) {
            const saved = sessionStorage.getItem(STORAGE_KEY);
            let data = saved ? JSON.parse(saved) : {};
            if (step === 1) {
                data.name = document.getElementById('name').value;
                data.email = document.getElementById('email').value;
            } else if (step === 2) {
                data.theme = document.getElementById('theme').value;
            }
            data.currentStep = step;
            sessionStorage.setItem(STORAGE_KEY, JSON.stringify(data));
        }
        function showStep(step) {
            document.querySelectorAll('.step-content').forEach(el => el.classList.remove('active'));
            document.getElementById('stepContent' + step).classList.add('active');
            for (let i = 1; i <= 3; i++) {
                const stepEl = document.getElementById('step' + i);
                stepEl.className = 'step';
                if (i < step) stepEl.classList.add('done');
                else if (i === step) stepEl.classList.add('active');
            }
        }
        function nextStep(currentStep) {
            saveStepData(currentStep);
            showStep(currentStep + 1);
        }
        function prevStep(currentStep) {
            saveStepData(currentStep);
            showStep(currentStep - 1);
        }
        function submitForm() {
            const data = JSON.parse(sessionStorage.getItem(STORAGE_KEY));
            console.log('提交数据:', data);
            sessionStorage.removeItem(STORAGE_KEY);
            alert('提交成功!');
        }
        loadFormData();
    </script>
</body>
</html>

示例二:页面状态保持与恢复

代码示例

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>SessionStorage - 页面状态保持</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body {
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
            background: #f5f7fa; min-height: 100vh; padding: 30px 20px;
        }
        .container { max-width: 640px; margin: 0 auto; }
        h1 { font-size: 24px; color: #2d3436; margin-bottom: 8px; }
        .desc { color: #636e72; font-size: 14px; margin-bottom: 24px; }
        .card {
            background: #fff; border-radius: 12px; padding: 24px;
            margin-bottom: 16px; box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
        }
        .card h2 { font-size: 16px; color: #2d3436; margin-bottom: 16px; }
        .tabs {
            display: flex; gap: 4px; background: #f0f0f0;
            border-radius: 8px; padding: 4px; margin-bottom: 16px;
        }
        .tab {
            flex: 1; padding: 8px; text-align: center;
            border-radius: 6px; font-size: 13px; font-weight: 600;
            cursor: pointer; color: #666;
        }
        .tab.active {
            background: #fff; color: #0984e3;
            box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
        }
        .tab-content { display: none; }
        .tab-content.active { display: block; }
    </style>
</head>
<body>
    <div class="container">
        <h1>页面状态保持与恢复</h1>
        <p class="desc">使用 sessionStorage 保存页面状态,刷新页面后自动恢复。</p>
        <div class="card">
            <h2>标签页状态</h2>
            <div class="tabs" id="tabs">
                <div class="tab active" data-tab="tab1" onclick="switchTab('tab1')">推荐</div>
                <div class="tab" data-tab="tab2" onclick="switchTab('tab2')">最新</div>
                <div class="tab" data-tab="tab3" onclick="switchTab('tab3')">热门</div>
            </div>
            <div class="tab-content active" id="tab1">
                <p style="color:#666;font-size:14px;">推荐内容区域</p>
            </div>
            <div class="tab-content" id="tab2">
                <p style="color:#666;font-size:14px;">最新内容区域</p>
            </div>
            <div class="tab-content" id="tab3">
                <p style="color:#666;font-size:14px;">热门内容区域</p>
            </div>
        </div>
    </div>
    <script>
        const STATE_KEY = 'pageState';
        function getSavedState() {
            try {
                const saved = sessionStorage.getItem(STATE_KEY);
                return saved ? JSON.parse(saved) : {};
            } catch (e) { return {}; }
        }
        function saveState(key, value) {
            const state = getSavedState();
            state[key] = value;
            sessionStorage.setItem(STATE_KEY, JSON.stringify(state));
        }
        function switchTab(tabId) {
            document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
            document.querySelector('.tab[data-tab="' + tabId + '"]').classList.add('active');
            document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
            document.getElementById(tabId).classList.add('active');
            saveState('activeTab', tabId);
        }
        function restoreState() {
            const state = getSavedState();
            if (state.activeTab) switchTab(state.activeTab);
        }
        restoreState();
    </script>
</body>
</html>

五、浏览器兼容性

浏览器 支持版本 备注
Chrome 5+ 完整支持
Firefox 2+ 完整支持
Safari 4+ 完整支持
Edge 12+ 完整支持
Opera 10.5+ 完整支持
IE 8+ 部分支持
iOS Safari 3.2+ 完整支持
Android 2.1+ 完整支持

六、注意事项与最佳实践

1. 数据生命周期

  • 标签页关闭即丢失:不要存储需要跨会话持久化的数据

  • 刷新不丢失:适合保存表单草稿等临时数据

  • 新标签页独立:每个标签页有独立的 sessionStorage,不会互相干扰

2. 容量管理

代码示例

// 检查存储是否可用
function checkSessionStorage() {
    try {
        sessionStorage.setItem('__test__', '1');
        sessionStorage.removeItem('__test__');
        return true;
    } catch (e) {
        return false;
    }
}

// 安全写入
function safeSessionSet(key, value) {
    try {
        sessionStorage.setItem(key, value);
        return true;
    } catch (e) {
        console.warn('sessionStorage 写入失败:', e);
        return false;
    }
}

3. 数据安全

  • sessionStorage 中的数据可被开发者工具直接查看和修改

  • 不要存储密码、密钥等敏感信息

  • 存储的数据在客户端,不要信任其完整性

4. 性能建议

  • 避免存储过大的数据(建议不超过 1MB)

  • 读写操作是同步的,大数据量会影响页面性能

  • 频繁读写时考虑使用内存变量缓存


七、代码规范示例

代码示例

// 推荐:封装 SessionStorage 工具类
class SessionStore {
    constructor(prefix = '') {
        this.prefix = prefix;
        this.available = this._checkAvailability();
    }

    _checkAvailability() {
        try {
            const key = '__ss_test__';
            sessionStorage.setItem(key, '1');
            sessionStorage.removeItem(key);
            return true;
        } catch (e) {
            console.warn('sessionStorage 不可用');
            return false;
        }
    }

    _getKey(key) {
        return this.prefix ? this.prefix + ':' + key : key;
    }

    set(key, value) {
        if (!this.available) return false;
        try {
            sessionStorage.setItem(this._getKey(key), JSON.stringify(value));
            return true;
        } catch (e) {
            return false;
        }
    }

    get(key, defaultValue = null) {
        if (!this.available) return defaultValue;
        try {
            const raw = sessionStorage.getItem(this._getKey(key));
            return raw !== null ? JSON.parse(raw) : defaultValue;
        } catch (e) {
            return defaultValue;
        }
    }

    remove(key) {
        if (!this.available) return;
        sessionStorage.removeItem(this._getKey(key));
    }

    clear() {
        if (!this.available) return;
        if (!this.prefix) {
            sessionStorage.clear();
            return;
        }
        const keys = [];
        for (let i = 0; i < sessionStorage.length; i++) {
            const k = sessionStorage.key(i);
            if (k.startsWith(this.prefix + ':')) keys.push(k);
        }
        keys.forEach(k => sessionStorage.removeItem(k));
    }
}

// 使用示例
const session = new SessionStore('myapp');
session.set('formDraft', { name: '张三', step: 2 });
const draft = session.get('formDraft', {});

八、常见问题与解决方案

常见问题

window.open 打开的新标签页能访问父页面的 sessionStorage 吗?

新标签页会获得父标签页 sessionStorage 的一份快照副本,但之后两者互不影响。修改一方的数据不会反映到另一方。

如何实现跨标签页的临时数据共享?

sessionStorage 本身不支持跨标签页共享。如需跨标签页通信,可使用:localStorage + storage 事件、BroadcastChannel API 或 SharedWorker

页面刷新后 sessionStorage 数据丢失?

原因:可能是浏览器隐私模式限制、代码中误调用了 sessionStorage.clear()、或浏览器设置中禁用了存储。
解决方案:检查隐私模式设置,排查代码中的 clear 调用。

如何检测 sessionStorage 是否被用户禁用?

解决方案:使用检测函数尝试写入和删除测试数据,捕获异常判断是否可用。


九、总结

SessionStorage 提供了会话级别的键值对存储,其标签页隔离特性使其非常适合存储临时性数据,如多步骤表单的中间状态、页面交互状态(标签页选中、筛选条件、勾选状态)、滚动位置等。使用时需注意其生命周期(标签页关闭即清除)、容量限制(5-10MB)和数据安全(不要存储敏感信息)。通过合理的封装和状态恢复机制,可以显著提升用户体验,避免因页面刷新导致的数据丢失。

标签: sessionStorage 会话存储 标签页隔离 多步骤表单 状态保持 临时数据

本文涉及AI创作

内容由AI创作,请仔细甄别

list快速访问

上一篇: HTML5 API:HTML5 Web Storage API - 完整教程与代码示例 下一篇: HTML5 API:HTML5 LocalStorage - 完整教程与代码示例

poll相关推荐