采用 Vue 3 实现单页应用(SPA)与本地数据存储方案

采用 Vue 3 实现单页应用(SPA)与本地数据存储方案

一、Vue 3 单页应用实现

核心工具:

  • Vue Router 4:路由管理
  • Pinia:状态管理(替代 Vuex)
  • Composition API:逻辑组织

实现步骤:

  1. 初始化项目

    bash 复制代码
    npm create vue@latest
    # 选择 Router + Pinia
  2. 路由配置 (src/router/index.js)

    javascript 复制代码
    import { createRouter, createWebHistory } from 'vue-router'
    import HomeView from '../views/HomeView.vue'
    
    const routes = [
      { path: '/', name: 'home', component: HomeView },
      { path: '/detail/:id', name: 'detail', component: () => import('../views/DetailView.vue') }
    ]
    
    const router = createRouter({
      history: createWebHistory(),
      routes
    })
    
    export default router
  3. 状态管理 (src/stores/dataStore.js)

    javascript 复制代码
    import { defineStore } from 'pinia'
    import { ref, computed } from 'vue'
    
    export const useDataStore = defineStore('data', () => {
      const localData = ref([])
      const lastUpdated = ref(null)
      const isLoading = ref(false)
    
      // 从本地存储加载数据
      const loadFromLocal = () => {
        const saved = localStorage.getItem('appData')
        if (saved) {
          const { data, timestamp } = JSON.parse(saved)
          localData.value = data
          lastUpdated.value = timestamp
        }
      }
    
      // 保存到本地存储
      const saveToLocal = (data) => {
        localData.value = data
        lastUpdated.value = Date.now()
        localStorage.setItem('appData', 
          JSON.stringify({ data, timestamp: lastUpdated.value })
        )
      }
    
      // 检查数据是否过期(示例:1小时)
      const isDataStale = computed(() => {
        return !lastUpdated.value || 
               (Date.now() - lastUpdated.value) > 3600000 // 1小时
      })
    
      // 获取数据(优先本地,必要时请求服务器)
      const fetchData = async () => {
        isLoading.value = true
        try {
          // 先尝试加载本地数据
          loadFromLocal()
          
          // 如果数据过期或不存在,请求服务器
          if (isDataStale.value || localData.value.length === 0) {
            const response = await fetch('https://api.example.com/data')
            const newData = await response.json()
            saveToLocal(newData)
          }
        } catch (error) {
          console.error('数据加载失败', error)
        } finally {
          isLoading.value = false
        }
      }
    
      return { 
        localData, 
        isLoading,
        fetchData,
        saveToLocal
      }
    })

二、本地数据存储策略

1. 存储方案选择

  • localStorage :适合 < 5MB 的简单数据

    javascript 复制代码
    // 存储
    localStorage.setItem('key', JSON.stringify(data))
    
    // 读取
    const data = JSON.parse(localStorage.getItem('key'))
  • IndexedDB :适合大型结构化数据(推荐使用 idb 库)

    bash 复制代码
    npm install idb
    javascript 复制代码
    import { openDB } from 'idb'
    
    const dbPromise = openDB('appDB', 1, {
      upgrade(db) {
        db.createObjectStore('dataStore')
      }
    })
    
    // 存储数据
    async function saveData(key, value) {
      const db = await dbPromise
      await db.put('dataStore', value, key)
    }
    
    // 读取数据
    async function getData(key) {
      const db = await dbPromise
      return db.get('dataStore', key)
    }

2. 数据更新策略

javascript 复制代码
// 在 Pinia store 中添加数据更新逻辑
const updateIfNeeded = async () => {
  if (navigator.onLine && isDataStale.value) {
    await fetchRemoteData()
  } else {
    loadFromLocal()
  }
}

// 组件中使用
import { useDataStore } from '@/stores/dataStore'

const store = useDataStore()
onMounted(() => {
  store.updateIfNeeded()
})

三、服务端交互优化

智能请求策略:

javascript 复制代码
// 在 Pinia store 中
const fetchRemoteData = async () => {
  try {
    const response = await fetch('/api/data', {
      headers: { 
        'If-Modified-Since': store.lastUpdated 
          ? new Date(store.lastUpdated).toUTCString() 
          : ''
      }
    })
    
    // 304 表示数据未修改
    if (response.status === 304) {
      store.lastUpdated = Date.now()
      return
    }
    
    const newData = await response.json()
    store.saveToLocal(newData)
  } catch (error) {
    console.error('远程请求失败', error)
  }
}

四、完整工作流程

  1. 应用启动时

    • 从 localStorage/IndexedDB 加载缓存数据
    • 检查数据时效性(如超过1小时)
    • 显示缓存数据的同时后台请求更新
  2. 数据操作时

    javascript 复制代码
    // 组件中的更新示例
    const handleUpdate = async (newItem) => {
      // 1. 立即更新本地数据(快速响应)
      const updatedData = [...store.localData, newItem]
      store.saveToLocal(updatedData)
      
      // 2. 后台同步到服务器
      try {
        await fetch('/api/update', {
          method: 'POST',
          body: JSON.stringify(newItem)
        })
      } catch (error) {
        // 失败时标记待同步状态
        store.queueSync(newItem)
      }
    }
  3. 网络恢复时

    javascript 复制代码
    // 监听网络状态
    window.addEventListener('online', () => {
      if (store.pendingSyncs.length > 0) {
        store.syncPendingData()
      }
      if (store.isDataStale) {
        store.fetchRemoteData()
      }
    })

五、最佳实践建议

  1. 数据版本控制

    javascript 复制代码
    localStorage.setItem('dataVersion', 'v2.1')
    // 升级时清除旧数据
    if (localStorage.getItem('dataVersion') !== currentVersion) {
      localStorage.clear()
    }
  2. 存储空间管理

    javascript 复制代码
    // 自动清理旧数据
    const MAX_ITEMS = 100
    if (localData.value.length > MAX_ITEMS) {
      saveToLocal(localData.value.slice(-MAX_ITEMS))
    }
  3. 敏感数据处理

    • 避免在本地存储敏感信息
    • 使用加密库(如 crypto-js)加密数据
    bash 复制代码
    npm install crypto-js
  4. 性能优化

    • 使用 Web Worker 处理 IndexedDB 操作
    • 对大数据集使用分页加载
    • 添加数据压缩(如 lz-string)
    bash 复制代码
    npm install lz-string

六、技术栈推荐

功能 推荐方案
状态管理 Pinia
本地数据库 IndexedDB + idb 库
数据同步 Axios + 指数退避重试
离线检测 navigator.onLine 事件
数据加密 crypto-js

这种架构提供:

  • ⚡️ 瞬时加载的本地数据体验
  • 📶 无缝的离线支持
  • 🔄 智能的后台同步机制
  • 📱 媲美原生应用的响应速度

通过合理平衡本地存储与服务端交互,可显著提升用户体验,同时减少服务器负载。

相关推荐
wyiyiyi22 分钟前
【Web后端】Django、flask及其场景——以构建系统原型为例
前端·数据库·后端·python·django·flask
gnip44 分钟前
vite和webpack打包结构控制
前端·javascript
excel1 小时前
在二维 Canvas 中模拟三角形绕 X、Y 轴旋转
前端
阿华的代码王国1 小时前
【Android】RecyclerView复用CheckBox的异常状态
android·xml·java·前端·后端
一条上岸小咸鱼1 小时前
Kotlin 基本数据类型(三):Booleans、Characters
android·前端·kotlin
Jimmy2 小时前
AI 代理是什么,其有助于我们实现更智能编程
前端·后端·ai编程
草梅友仁2 小时前
草梅 Auth 1.4.0 发布与 ESLint v9 更新 | 2025 年第 33 周草梅周报
vue.js·github·nuxt.js
ZXT2 小时前
promise & async await总结
前端
Jerry说前后端2 小时前
RecyclerView 性能优化:从原理到实践的深度优化方案
android·前端·性能优化
画个太阳作晴天2 小时前
A12预装app
linux·服务器·前端