Skip to content

会话存储 (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 StorageSession Storage
生命周期永久(除非手动清除)会话期间
作用域同源下所有标签页共享仅当前标签页
适用场景用户偏好、长期数据临时数据、表单草稿

最佳实践

  1. 用于临时数据:Session Storage 适合存储临时性数据
  2. 表单草稿:自动保存用户输入,防止意外丢失
  3. 页面状态:保持用户的浏览状态和选择
  4. 缓存优化:缓存会话期间的 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 StorageLocal StorageCookie
生命周期会话期间永久可配置
作用域当前标签页同源所有标签页同源所有标签页
容量~5MB~5MB~4KB
服务器访问
适用场景临时数据持久化数据身份认证

注意事项

  1. 隐私模式限制:某些浏览器的隐私模式可能限制 Session Storage
  2. 容量限制:不同浏览器的容量限制可能不同
  3. 同步问题:Session Storage 在同一标签页的不同 iframe 间是共享的
  4. 序列化限制:只能存储可序列化的数据
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

Released under the MIT License.