sessionStorage
sessionStorage 是 HTML5 Web Storage API 的另一成员,与 localStorage 使用相同的 API,但数据生命周期完全不同。sessionStorage 的数据仅在当前浏览器标签页(会话)中有效,关闭标签页后数据自动清除。它适用于临时存储表单数据、页面状态、一次性操作数据等场景。
前置知识
阅读本节前,建议先了解:localStorage
基础概念
什么是 sessionStorage
sessionStorage 存储的数据具有"会话级"生命周期,从用户打开页面开始,到关闭标签页或浏览器结束。它与 localStorage 共享完全相同的 API 接口,但在数据持久性和作用范围上存在本质区别。
与 localStorage 对比
| 特性 | localStorage | sessionStorage |
|---|---|---|
| 生命周期 | 永久(手动清除前) | 会话级(关闭标签页即清除) |
| 作用范围 | 同源所有标签页共享 | 仅限当前标签页 |
| 存储容量 | 约 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 及以下不支持 |
| 移动端 | 支持 | 后台标签页可能被系统回收 |
最佳实践
- 适合 sessionStorage 的场景:表单暂存、一次性向导、页面状态、临时过滤条件
- 不适合 sessionStorage 的场景:需要跨标签页的数据、长期缓存、大量数据存储
- 刷新恢复:利用页面刷新时 sessionStorage 不清除的特性,实现表单数据恢复
- 及时清理:操作完成后主动
removeItem或clear,避免残留数据
下一节
继续学习:Storage API 详解