采用 Vue 3 实现单页应用(SPA)与本地数据存储方案
一、Vue 3 单页应用实现
核心工具:
- Vue Router 4:路由管理
- Pinia:状态管理(替代 Vuex)
- Composition API:逻辑组织
实现步骤:
-
初始化项目
bashnpm create vue@latest # 选择 Router + Pinia
-
路由配置 (
src/router/index.js
)javascriptimport { 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
-
状态管理 (
src/stores/dataStore.js
)javascriptimport { 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 库)
bashnpm install idb
javascriptimport { 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)
}
}
四、完整工作流程
-
应用启动时:
- 从 localStorage/IndexedDB 加载缓存数据
- 检查数据时效性(如超过1小时)
- 显示缓存数据的同时后台请求更新
-
数据操作时:
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) } }
-
网络恢复时:
javascript// 监听网络状态 window.addEventListener('online', () => { if (store.pendingSyncs.length > 0) { store.syncPendingData() } if (store.isDataStale) { store.fetchRemoteData() } })
五、最佳实践建议
-
数据版本控制:
javascriptlocalStorage.setItem('dataVersion', 'v2.1') // 升级时清除旧数据 if (localStorage.getItem('dataVersion') !== currentVersion) { localStorage.clear() }
-
存储空间管理:
javascript// 自动清理旧数据 const MAX_ITEMS = 100 if (localData.value.length > MAX_ITEMS) { saveToLocal(localData.value.slice(-MAX_ITEMS)) }
-
敏感数据处理:
- 避免在本地存储敏感信息
- 使用加密库(如 crypto-js)加密数据
bashnpm install crypto-js
-
性能优化:
- 使用 Web Worker 处理 IndexedDB 操作
- 对大数据集使用分页加载
- 添加数据压缩(如 lz-string)
bashnpm install lz-string
六、技术栈推荐
功能 | 推荐方案 |
---|---|
状态管理 | Pinia |
本地数据库 | IndexedDB + idb 库 |
数据同步 | Axios + 指数退避重试 |
离线检测 | navigator.onLine 事件 |
数据加密 | crypto-js |
这种架构提供:
- ⚡️ 瞬时加载的本地数据体验
- 📶 无缝的离线支持
- 🔄 智能的后台同步机制
- 📱 媲美原生应用的响应速度
通过合理平衡本地存储与服务端交互,可显著提升用户体验,同时减少服务器负载。