会话存储 (Session Storage)
@pt/utils/modules/storage 提供了会话级存储封装,数据在页面会话期间保留,关闭标签页或浏览器后自动清除。
基本概念
Session Storage 与 Local Storage 类似,但有以下关键区别:
- 生命周期:仅在当前浏览器标签页或窗口会话期间有效
- 作用域:数据不在不同标签页或窗口间共享
- 用途:适合存储临时数据、表单草稿、页面状态等
基本用法
typescript
import { createSessionStorage } from '@pt/utils/modules/storage';
// 创建会话存储实例
const sessionStorage = createSessionStorage('app:');
// 设置数据
sessionStorage.set('formData', { name: 'John', email: 'john@example.com' });
// 获取数据
const formData = sessionStorage.get('formData');
// 移除数据
sessionStorage.remove('formData');
// 清除所有数据
sessionStorage.clear();主要特性
1. 自动过期
支持设置数据的过期时间。
typescript
const storage = createSessionStorage('temp:');
// 设置10分钟后过期
storage.set('tempData', { value: 123 }, 10 * 60 * 1000);2. 前缀隔离
使用前缀避免不同功能模块的数据冲突。
typescript
const wizardStorage = createSessionStorage('wizard:');
const formStorage = createSessionStorage('form:');3. 类型安全
完整的 TypeScript 支持。
typescript
interface WizardState {
currentStep: number;
formData: Record<string, any>;
}
const storage = createSessionStorage('wizard:');
storage.set<WizardState>('state', { currentStep: 2, formData: {} });API 参考
Session Storage 与 Local Storage 共享相同的 API,详细文档请参考 Local Storage。
使用场景
1. 多步骤表单/向导
typescript
const wizardStorage = createSessionStorage('wizard:');
function saveWizardStep(step: number, data: any) {
const wizardData = wizardStorage.get('data') || { step: 1 };
wizardData.step = step;
wizardData[`step${step}`] = data;
wizardStorage.set('data', wizardData);
}2. 表单草稿自动保存
typescript
import { ref, watch } from 'vue';
const formStorage = createSessionStorage('form:');
const formData = ref({ title: '', content: '' });
// 自动保存草稿
watch(formData, (newValue) => {
formStorage.set('draft', newValue);
}, { deep: true });
// 加载草稿
const draft = formStorage.get('draft');
if (draft) formData.value = draft;3. 临时缓存
typescript
const cacheStorage = createSessionStorage('cache:');
async function fetchData(id: string) {
const cached = cacheStorage.get(`data:${id}`);
if (cached) return cached;
const data = await fetch(`/api/data/${id}`).then(r => r.json());
cacheStorage.set(`data:${id}`, data, 5 * 60 * 1000); // 5分钟
return data;
}4. 页面状态保持
typescript
const pageStorage = createSessionStorage('page:');
// 保存状态
function savePageState() {
pageStorage.set('state', {
scrollPosition: window.scrollY,
selectedTab: currentTab.value
});
}
// 恢复状态
function restorePageState() {
const state = pageStorage.get('state');
if (state) {
window.scrollTo(0, state.scrollPosition);
currentTab.value = state.selectedTab;
}
}Local Storage vs Session Storage
| 特性 | Local Storage | Session Storage |
|---|---|---|
| 生命周期 | 永久(除非手动清除) | 会话期间 |
| 作用域 | 同源下所有标签页共享 | 仅当前标签页 |
| 适用场景 | 用户偏好、长期数据 | 临时数据、表单草稿 |
最佳实践
- 用于临时数据:Session Storage 适合存储临时性数据
- 表单草稿:自动保存用户输入,防止意外丢失
- 页面状态:保持用户的浏览状态和选择
- 缓存优化:缓存会话期间的 API 响应
typescript
// ✅ 推荐:临时数据
sessionStorage.set('wizardStep', currentStep);
sessionStorage.set('formDraft', formData);
// ❌ 不推荐:长期数据应使用 localStorage完整示例
多步骤向导完整实现
typescript
import { createSessionStorage } from '@pt/utils/modules/storage';
import { ref, computed } from 'vue';
interface WizardData {
step: number;
personalInfo?: {
name: string;
email: string;
phone: string;
};
preferences?: {
theme: string;
language: string;
};
completed: boolean;
}
const wizardStorage = createSessionStorage('wizard:');
const wizardData = ref<WizardData>(wizardStorage.get('data') || { step: 1, completed: false });
// 保存步骤数据
function saveStep(step: number, data: any) {
wizardData.value.step = step;
if (step === 1) wizardData.value.personalInfo = data;
if (step === 2) wizardData.value.preferences = data;
wizardStorage.set('data', wizardData.value);
}
// 完成向导
function completeWizard() {
wizardData.value.completed = true;
wizardStorage.clear();
}
// 重置向导
function resetWizard() {
wizardData.value = { step: 1, completed: false };
wizardStorage.clear();
}表单自动保存完整实现
typescript
import { createSessionStorage } from '@pt/utils/modules/storage';
import { ref, watch, onMounted } from 'vue';
const formStorage = createSessionStorage('form:');
interface ArticleForm {
title: string;
content: string;
category: string;
tags: string[];
}
const formData = ref<ArticleForm>({
title: '',
content: '',
category: '',
tags: []
});
// 自动保存(防抖)
let saveTimer: ReturnType<typeof setTimeout>;
watch(formData, (newValue) => {
clearTimeout(saveTimer);
saveTimer = setTimeout(() => {
formStorage.set('draft', newValue);
console.log('草稿已自动保存');
}, 1000);
}, { deep: true });
// 加载草稿
onMounted(() => {
const draft = formStorage.get<ArticleForm>('draft');
if (draft) {
formData.value = draft;
console.log('已恢复草稿');
}
});
// 提交表单
async function submitForm() {
try {
await api.saveArticle(formData.value);
formStorage.remove('draft');
console.log('文章已发布,草稿已清除');
} catch (error) {
console.error('发布失败,草稿已保留');
}
}与其他存储方案对比
| 特性 | Session Storage | Local Storage | Cookie |
|---|---|---|---|
| 生命周期 | 会话期间 | 永久 | 可配置 |
| 作用域 | 当前标签页 | 同源所有标签页 | 同源所有标签页 |
| 容量 | ~5MB | ~5MB | ~4KB |
| 服务器访问 | 否 | 否 | 是 |
| 适用场景 | 临时数据 | 持久化数据 | 身份认证 |
注意事项
- 隐私模式限制:某些浏览器的隐私模式可能限制 Session Storage
- 容量限制:不同浏览器的容量限制可能不同
- 同步问题:Session Storage 在同一标签页的不同 iframe 间是共享的
- 序列化限制:只能存储可序列化的数据
typescript
// ✅ 可以存储
sessionStorage.set('data', { name: 'John', age: 30 });
sessionStorage.set('list', [1, 2, 3]);
sessionStorage.set('date', new Date().toISOString());
// ❌ 不能直接存储
sessionStorage.set('func', () => {}); // 函数会丢失
sessionStorage.set('date', new Date()); // Date 对象会被转为字符串
sessionStorage.set('map', new Map()); // Map 会变成空对象
sessionStorage.set('userPreferences', prefs); // 应该用 localStorage