Files
MoviePilot-Frontend/PWA_State_Management_Implementation.md
2025-07-06 06:44:06 +00:00

7.0 KiB
Raw Blame History

PWA状态管理功能实现说明

概述

本次实现为您的MoviePilot项目添加了完整的PWA状态管理功能专门解决iOS设备上PWA后台被杀导致状态丢失的问题。

已实现的功能

1. 核心状态管理器 (src/utils/pwaStateManager.ts)

  • 多层存储策略localStorage + sessionStorage + IndexedDB + Service Worker缓存
  • 智能状态恢复基于时间、URL、设备方向的智能决策
  • 生命周期监听:自动监听页面可见性、焦点变化、卸载事件
  • 表单状态保存:自动保存和恢复表单数据
  • 滚动位置恢复:精确恢复页面滚动位置
  • UI状态管理:保存侧边栏、主题等界面状态

2. Service Worker增强 (src/service-worker.ts)

  • 状态缓存端点:虚拟的/api/pwa-state端点用于状态存储
  • 消息通信:支持与主应用的双向状态同步
  • 缓存管理:专用的状态缓存空间
  • 错误处理:完善的错误处理和降级策略

3. Vue集成 (src/main.ts)

  • 自动初始化PWA模式下自动启动状态管理
  • 环境检测智能检测PWA运行环境
  • 全局可用:状态管理器绑定到全局对象
  • 事件监听:监听状态恢复事件并处理

4. Vue组合式API (src/composables/usePWAState.ts)

  • 响应式状态:提供响应式的状态管理接口
  • 便捷方法:封装常用的状态操作方法
  • 类型安全完整的TypeScript类型支持
  • 组件友好易于在Vue组件中使用

5. 类型声明 (src/types/pwa.d.ts)

  • 类型扩展扩展Window和Navigator接口
  • 自定义事件:定义状态恢复事件类型
  • TypeScript支持:完整的类型安全保障

6. 演示组件 (src/components/PWAStateDemo.vue)

  • 功能演示:展示所有状态管理功能
  • 测试界面:提供测试表单和操作按钮
  • 状态监控:实时显示状态管理器状态

使用方法

在Vue组件中使用

<script setup lang="ts">
import { usePWAState } from '@/composables/usePWAState'

const {
  isPWAMode,
  isStateRestored,
  saveCurrentState,
  resetStateRestored
} = usePWAState()

// 手动保存状态
const handleImportantAction = async () => {
  await saveCurrentState()
  // 执行重要操作
}
</script>

<template>
  <div>
    <VAlert v-if="isStateRestored" type="success">
      状态已恢复
    </VAlert>
    
    <VBtn @click="handleImportantAction">
      执行重要操作
    </VBtn>
  </div>
</template>

手动操作状态

// 在浏览器控制台中
window.pwaStateController.saveCurrentState()  // 保存当前状态

工作原理

1. 状态保存时机

  • 🔄 页面隐藏时visibilitychange事件触发
  • 🔄 失去焦点时blur事件延迟1秒触发
  • 🔄 页面卸载时beforeunload事件触发
  • 🔄 定期保存每30秒自动保存一次
  • 🔄 手动触发调用API手动保存

2. 状态恢复时机

  • 🔄 应用启动时:自动检查并恢复状态
  • 🔄 页面显示时visibilitychange事件触发
  • 🔄 获得焦点时:清除延迟保存定时器

3. 存储策略

localStorage (主要状态)
    ↓
sessionStorage (临时状态)
    ↓
IndexedDB (大量数据)
    ↓
Service Worker缓存 (跨页面共享)

4. 恢复决策

状态恢复需要同时满足:

  • 状态未过期默认30分钟内
  • URL路径匹配
  • 设备方向未显著变化

配置选项

修改状态保存间隔

// 在 PWAStateController 中修改
private setupPeriodicSave(): void {
  setInterval(() => {
    if (!document.hidden) {
      this.saveCurrentState()
    }
  }, 60000) // 改为60秒保存一次
}

修改状态过期时间

// 在 StateRestoreDecision 中修改
export class StateRestoreDecision {
  private maxStateAge = 60 * 60 * 1000 // 改为60分钟
}

自定义状态内容

// 在 PWAStateController 中添加自定义状态
private getAppSpecificState(): any {
  return {
    // 现有状态...
    
    // 添加自定义状态
    customData: {
      userPreferences: this.getUserPreferences(),
      currentMedia: this.getCurrentMedia(),
      searchHistory: this.getSearchHistory()
    }
  }
}

调试和监控

控制台调试

// 检查状态管理器是否可用
console.log('状态管理器:', window.pwaStateController)

// 查看当前保存的状态
console.log('本地状态:', localStorage.getItem('mp-pwa-app-state'))

// 手动保存状态
window.pwaStateController?.saveCurrentState()

// 清除所有状态
localStorage.removeItem('mp-pwa-app-state')
sessionStorage.removeItem('mp-pwa-session-state')

监听状态事件

// 监听状态恢复事件
window.addEventListener('pwaStateRestored', (event) => {
  console.log('状态已恢复:', event.detail.state)
})

注意事项

iOS特殊性

  • PWA不与Safari共享存储空间
  • 后台执行时间有限约30秒-5分钟
  • 内存压力时会被强制清理
  • Service Worker可能会被暂停

存储限制

  • localStorage: 约5-10MB
  • sessionStorage: 约5-10MB
  • IndexedDB: 较大但可能被清理
  • Service Worker缓存: 约50MB

性能考虑

  • 避免保存过大的状态对象
  • 使用防抖技术避免频繁保存
  • 异步处理状态操作
  • 定期清理过期状态

测试方法

1. 基本功能测试

  1. 将应用添加到iOS桌面
  2. 打开PWA填写测试表单
  3. 切换到其他应用
  4. 等待几分钟后重新打开PWA
  5. 检查表单数据和滚动位置是否恢复

2. 状态管理测试

  1. 在PWA中访问演示页面/pwa-state-demo
  2. 观察状态管理器状态
  3. 测试手动保存和恢复功能
  4. 验证状态恢复通知

3. 长时间测试

  1. 保持PWA在后台运行几小时
  2. 使用其他应用增加内存压力
  3. 重新打开PWA检查状态恢复
  4. 重启设备后测试状态持久性

故障排除

状态未恢复

  1. 检查是否在PWA模式运行
  2. 确认状态管理器已初始化
  3. 检查控制台错误信息
  4. 验证存储权限和配额

性能问题

  1. 减少状态保存频率
  2. 优化状态对象大小
  3. 检查内存使用情况
  4. 考虑延迟初始化

兼容性问题

  1. 检查iOS版本支持
  2. 验证Service Worker注册
  3. 测试不同设备和浏览器
  4. 检查网络连接状态

后续优化建议

  1. 智能压缩:对大型状态对象进行压缩
  2. 增量保存:只保存变化的状态部分
  3. 云端同步:结合服务器实现跨设备状态同步
  4. 用户偏好:允许用户自定义状态保存策略
  5. 性能监控:添加状态管理性能指标
  6. A/B测试:测试不同的状态管理策略效果

通过这套完整的解决方案您的MoviePilot PWA应该能够在iOS设备上提供更好的用户体验显著减少因后台被杀而导致的状态丢失问题。