Search K
Appearance
Appearance
📊 SEO元描述:2024年最新数据缓存策略教程,详解Vue项目缓存实现、缓存策略、离线存储。包含完整缓存案例,适合Vue.js开发者优化数据加载性能和用户体验。
核心关键词:数据缓存策略 2024、Vue数据缓存、前端缓存优化、API缓存、离线存储、Vue性能优化
长尾关键词:Vue数据缓存怎么实现、前端缓存策略设计、API数据缓存、Vue离线存储、数据缓存最佳实践
通过本节数据缓存策略,你将系统性掌握:
数据缓存策略是什么?这是现代Web应用性能优化的核心技术。数据缓存策略是系统性地存储和管理数据副本,以减少网络请求和提升用户体验的完整方案,也是高性能Web应用的重要基础设施。
💡 学习建议:缓存策略需要根据具体业务场景设计,建议从简单的内存缓存开始,逐步构建完整的缓存体系
// 🎉 缓存策略类型分析
const cacheStrategies = {
// 缓存优先策略
cacheFirst: {
description: '优先使用缓存,缓存不存在时请求网络',
useCases: ['静态资源', '不常变化的数据', '用户配置'],
pros: ['响应速度快', '减少网络请求'],
cons: ['可能获取过时数据']
},
// 网络优先策略
networkFirst: {
description: '优先请求网络,网络失败时使用缓存',
useCases: ['实时数据', '重要业务数据', '用户状态'],
pros: ['数据最新', '保证准确性'],
cons: ['网络依赖性强', '响应较慢']
},
// 仅缓存策略
cacheOnly: {
description: '只使用缓存,不发起网络请求',
useCases: ['离线模式', '预加载数据', '静态配置'],
pros: ['完全离线', '响应极快'],
cons: ['数据可能过时', '依赖预加载']
},
// 仅网络策略
networkOnly: {
description: '只使用网络请求,不使用缓存',
useCases: ['敏感数据', '一次性操作', '实时计算'],
pros: ['数据最新', '安全性高'],
cons: ['网络依赖', '性能较差']
},
// 过期重新验证策略
staleWhileRevalidate: {
description: '返回缓存数据,同时在后台更新缓存',
useCases: ['新闻列表', '商品信息', '用户资料'],
pros: ['响应快', '数据相对新'],
cons: ['实现复杂', '可能显示过时数据']
}
}// 🎉 基础内存缓存实现
class MemoryCache {
constructor(options = {}) {
this.cache = new Map()
this.maxSize = options.maxSize || 100
this.defaultTTL = options.defaultTTL || 5 * 60 * 1000 // 5分钟
this.cleanupInterval = options.cleanupInterval || 60 * 1000 // 1分钟
// 启动定期清理
this.startCleanup()
}
// 设置缓存
set(key, value, ttl = this.defaultTTL) {
// 检查缓存大小限制
if (this.cache.size >= this.maxSize) {
this.evictOldest()
}
const item = {
value,
timestamp: Date.now(),
ttl,
accessCount: 0,
lastAccess: Date.now()
}
this.cache.set(key, item)
return this
}
// 获取缓存
get(key) {
const item = this.cache.get(key)
if (!item) {
return null
}
// 检查是否过期
if (this.isExpired(item)) {
this.cache.delete(key)
return null
}
// 更新访问信息
item.accessCount++
item.lastAccess = Date.now()
return item.value
}
// 检查是否存在
has(key) {
const item = this.cache.get(key)
if (!item) {
return false
}
if (this.isExpired(item)) {
this.cache.delete(key)
return false
}
return true
}
// 删除缓存
delete(key) {
return this.cache.delete(key)
}
// 清空缓存
clear() {
this.cache.clear()
return this
}
// 检查是否过期
isExpired(item) {
return Date.now() - item.timestamp > item.ttl
}
// 淘汰最旧的缓存项
evictOldest() {
let oldestKey = null
let oldestTime = Date.now()
for (const [key, item] of this.cache) {
if (item.lastAccess < oldestTime) {
oldestTime = item.lastAccess
oldestKey = key
}
}
if (oldestKey) {
this.cache.delete(oldestKey)
}
}
// 清理过期缓存
cleanup() {
const now = Date.now()
for (const [key, item] of this.cache) {
if (now - item.timestamp > item.ttl) {
this.cache.delete(key)
}
}
}
// 启动定期清理
startCleanup() {
this.cleanupTimer = setInterval(() => {
this.cleanup()
}, this.cleanupInterval)
}
// 停止定期清理
stopCleanup() {
if (this.cleanupTimer) {
clearInterval(this.cleanupTimer)
this.cleanupTimer = null
}
}
// 获取缓存统计信息
getStats() {
let totalAccess = 0
let expiredCount = 0
const now = Date.now()
for (const item of this.cache.values()) {
totalAccess += item.accessCount
if (now - item.timestamp > item.ttl) {
expiredCount++
}
}
return {
size: this.cache.size,
maxSize: this.maxSize,
totalAccess,
expiredCount,
hitRate: totalAccess > 0 ? (totalAccess - expiredCount) / totalAccess : 0
}
}
}
// 创建全局缓存实例
export const memoryCache = new MemoryCache({
maxSize: 200,
defaultTTL: 10 * 60 * 1000, // 10分钟
cleanupInterval: 2 * 60 * 1000 // 2分钟清理一次
})// 🎉 LRU (Least Recently Used) 缓存实现
class LRUCache {
constructor(capacity = 50) {
this.capacity = capacity
this.cache = new Map()
}
get(key) {
if (this.cache.has(key)) {
// 移动到最新位置
const value = this.cache.get(key)
this.cache.delete(key)
this.cache.set(key, value)
return value
}
return null
}
set(key, value) {
if (this.cache.has(key)) {
// 更新现有键
this.cache.delete(key)
} else if (this.cache.size >= this.capacity) {
// 删除最久未使用的项
const firstKey = this.cache.keys().next().value
this.cache.delete(firstKey)
}
this.cache.set(key, value)
}
has(key) {
return this.cache.has(key)
}
delete(key) {
return this.cache.delete(key)
}
clear() {
this.cache.clear()
}
size() {
return this.cache.size
}
keys() {
return Array.from(this.cache.keys())
}
values() {
return Array.from(this.cache.values())
}
}
export const lruCache = new LRUCache(100)// 🎉 LocalStorage缓存管理器
class LocalStorageCache {
constructor(prefix = 'app_cache_') {
this.prefix = prefix
this.isSupported = this.checkSupport()
}
// 检查LocalStorage支持
checkSupport() {
try {
const testKey = '__test__'
localStorage.setItem(testKey, 'test')
localStorage.removeItem(testKey)
return true
} catch (e) {
return false
}
}
// 生成完整键名
getKey(key) {
return `${this.prefix}${key}`
}
// 设置缓存
set(key, value, ttl = null) {
if (!this.isSupported) {
console.warn('LocalStorage not supported')
return false
}
try {
const item = {
value,
timestamp: Date.now(),
ttl
}
localStorage.setItem(this.getKey(key), JSON.stringify(item))
return true
} catch (e) {
console.error('Failed to set localStorage cache:', e)
// 存储空间不足时清理过期缓存
if (e.name === 'QuotaExceededError') {
this.cleanup()
// 重试一次
try {
localStorage.setItem(this.getKey(key), JSON.stringify({
value,
timestamp: Date.now(),
ttl
}))
return true
} catch (retryError) {
console.error('Retry failed:', retryError)
}
}
return false
}
}
// 获取缓存
get(key) {
if (!this.isSupported) {
return null
}
try {
const itemStr = localStorage.getItem(this.getKey(key))
if (!itemStr) {
return null
}
const item = JSON.parse(itemStr)
// 检查是否过期
if (item.ttl && Date.now() - item.timestamp > item.ttl) {
this.delete(key)
return null
}
return item.value
} catch (e) {
console.error('Failed to get localStorage cache:', e)
return null
}
}
// 检查是否存在
has(key) {
return this.get(key) !== null
}
// 删除缓存
delete(key) {
if (!this.isSupported) {
return false
}
try {
localStorage.removeItem(this.getKey(key))
return true
} catch (e) {
console.error('Failed to delete localStorage cache:', e)
return false
}
}
// 清空所有缓存
clear() {
if (!this.isSupported) {
return false
}
try {
const keys = Object.keys(localStorage)
keys.forEach(key => {
if (key.startsWith(this.prefix)) {
localStorage.removeItem(key)
}
})
return true
} catch (e) {
console.error('Failed to clear localStorage cache:', e)
return false
}
}
// 清理过期缓存
cleanup() {
if (!this.isSupported) {
return
}
const keys = Object.keys(localStorage)
const now = Date.now()
keys.forEach(key => {
if (key.startsWith(this.prefix)) {
try {
const itemStr = localStorage.getItem(key)
const item = JSON.parse(itemStr)
if (item.ttl && now - item.timestamp > item.ttl) {
localStorage.removeItem(key)
}
} catch (e) {
// 解析失败的项目也删除
localStorage.removeItem(key)
}
}
})
}
// 获取缓存大小
getSize() {
if (!this.isSupported) {
return 0
}
let size = 0
const keys = Object.keys(localStorage)
keys.forEach(key => {
if (key.startsWith(this.prefix)) {
size += localStorage.getItem(key).length
}
})
return size
}
// 获取所有缓存键
getKeys() {
if (!this.isSupported) {
return []
}
const keys = Object.keys(localStorage)
return keys
.filter(key => key.startsWith(this.prefix))
.map(key => key.replace(this.prefix, ''))
}
}
export const localCache = new LocalStorageCache('vue_app_')// 🎉 智能API缓存管理器
import axios from 'axios'
import { memoryCache } from './memoryCache'
import { localCache } from './localStorageCache'
class ApiCacheManager {
constructor() {
this.strategies = {
CACHE_FIRST: 'cache-first',
NETWORK_FIRST: 'network-first',
CACHE_ONLY: 'cache-only',
NETWORK_ONLY: 'network-only',
STALE_WHILE_REVALIDATE: 'stale-while-revalidate'
}
this.defaultConfig = {
strategy: this.strategies.CACHE_FIRST,
memoryTTL: 5 * 60 * 1000, // 5分钟
storageTTL: 30 * 60 * 1000, // 30分钟
useMemory: true,
useStorage: true,
revalidateInBackground: false
}
}
// 生成缓存键
generateCacheKey(url, params = {}, method = 'GET') {
const sortedParams = Object.keys(params)
.sort()
.reduce((result, key) => {
result[key] = params[key]
return result
}, {})
return `${method}:${url}:${JSON.stringify(sortedParams)}`
}
// 发送带缓存的请求
async request(url, options = {}) {
const config = { ...this.defaultConfig, ...options }
const { method = 'GET', params = {}, data } = config
// 只对GET请求使用缓存
if (method.toUpperCase() !== 'GET') {
return this.makeNetworkRequest(url, config)
}
const cacheKey = this.generateCacheKey(url, params, method)
switch (config.strategy) {
case this.strategies.CACHE_FIRST:
return this.cacheFirstStrategy(url, cacheKey, config)
case this.strategies.NETWORK_FIRST:
return this.networkFirstStrategy(url, cacheKey, config)
case this.strategies.CACHE_ONLY:
return this.cacheOnlyStrategy(cacheKey)
case this.strategies.NETWORK_ONLY:
return this.networkOnlyStrategy(url, config)
case this.strategies.STALE_WHILE_REVALIDATE:
return this.staleWhileRevalidateStrategy(url, cacheKey, config)
default:
return this.cacheFirstStrategy(url, cacheKey, config)
}
}
// 缓存优先策略
async cacheFirstStrategy(url, cacheKey, config) {
// 先检查内存缓存
if (config.useMemory) {
const memoryData = memoryCache.get(cacheKey)
if (memoryData) {
return { data: memoryData, fromCache: true, cacheType: 'memory' }
}
}
// 再检查持久化缓存
if (config.useStorage) {
const storageData = localCache.get(cacheKey)
if (storageData) {
// 同时存入内存缓存
if (config.useMemory) {
memoryCache.set(cacheKey, storageData, config.memoryTTL)
}
return { data: storageData, fromCache: true, cacheType: 'storage' }
}
}
// 缓存未命中,发起网络请求
try {
const response = await this.makeNetworkRequest(url, config)
this.setCacheData(cacheKey, response.data, config)
return { data: response.data, fromCache: false }
} catch (error) {
throw error
}
}
// 网络优先策略
async networkFirstStrategy(url, cacheKey, config) {
try {
const response = await this.makeNetworkRequest(url, config)
this.setCacheData(cacheKey, response.data, config)
return { data: response.data, fromCache: false }
} catch (error) {
// 网络失败,尝试使用缓存
const cachedData = this.getCacheData(cacheKey, config)
if (cachedData) {
return { data: cachedData.data, fromCache: true, cacheType: cachedData.type }
}
throw error
}
}
// 仅缓存策略
async cacheOnlyStrategy(cacheKey) {
const cachedData = this.getCacheData(cacheKey, { useMemory: true, useStorage: true })
if (cachedData) {
return { data: cachedData.data, fromCache: true, cacheType: cachedData.type }
}
throw new Error('No cached data available')
}
// 仅网络策略
async networkOnlyStrategy(url, config) {
const response = await this.makeNetworkRequest(url, config)
return { data: response.data, fromCache: false }
}
// 过期重新验证策略
async staleWhileRevalidateStrategy(url, cacheKey, config) {
const cachedData = this.getCacheData(cacheKey, config)
if (cachedData) {
// 返回缓存数据
const result = { data: cachedData.data, fromCache: true, cacheType: cachedData.type }
// 在后台更新缓存
if (config.revalidateInBackground) {
this.revalidateInBackground(url, cacheKey, config)
}
return result
} else {
// 没有缓存,发起网络请求
const response = await this.makeNetworkRequest(url, config)
this.setCacheData(cacheKey, response.data, config)
return { data: response.data, fromCache: false }
}
}
// 后台重新验证
async revalidateInBackground(url, cacheKey, config) {
try {
const response = await this.makeNetworkRequest(url, config)
this.setCacheData(cacheKey, response.data, config)
} catch (error) {
console.warn('Background revalidation failed:', error)
}
}
// 发起网络请求
async makeNetworkRequest(url, config) {
const axiosConfig = {
method: config.method || 'GET',
url,
params: config.params,
data: config.data,
headers: config.headers,
timeout: config.timeout || 10000
}
return axios(axiosConfig)
}
// 设置缓存数据
setCacheData(cacheKey, data, config) {
if (config.useMemory) {
memoryCache.set(cacheKey, data, config.memoryTTL)
}
if (config.useStorage) {
localCache.set(cacheKey, data, config.storageTTL)
}
}
// 获取缓存数据
getCacheData(cacheKey, config) {
if (config.useMemory) {
const memoryData = memoryCache.get(cacheKey)
if (memoryData) {
return { data: memoryData, type: 'memory' }
}
}
if (config.useStorage) {
const storageData = localCache.get(cacheKey)
if (storageData) {
return { data: storageData, type: 'storage' }
}
}
return null
}
// 清除特定缓存
clearCache(url, params = {}, method = 'GET') {
const cacheKey = this.generateCacheKey(url, params, method)
memoryCache.delete(cacheKey)
localCache.delete(cacheKey)
}
// 清除所有缓存
clearAllCache() {
memoryCache.clear()
localCache.clear()
}
// 获取缓存统计
getCacheStats() {
return {
memory: memoryCache.getStats(),
storage: {
size: localCache.getSize(),
keys: localCache.getKeys().length
}
}
}
}
export const apiCache = new ApiCacheManager()<!-- 🎉 使用缓存的Vue组件 -->
<template>
<div class="cached-data-demo">
<div class="controls">
<button @click="refreshData" :disabled="loading">
{{ loading ? '加载中...' : '刷新数据' }}
</button>
<button @click="clearCache">
清除缓存
</button>
<select v-model="cacheStrategy" @change="loadData">
<option value="cache-first">缓存优先</option>
<option value="network-first">网络优先</option>
<option value="stale-while-revalidate">过期重新验证</option>
</select>
</div>
<div class="data-info">
<p>数据来源: {{ dataSource }}</p>
<p>加载时间: {{ loadTime }}ms</p>
<p>缓存统计: {{ cacheStats }}</p>
</div>
<div v-if="error" class="error">
{{ error }}
</div>
<div v-else-if="data" class="data-display">
<h3>用户列表</h3>
<div
v-for="user in data.users"
:key="user.id"
class="user-item"
>
<h4>{{ user.name }}</h4>
<p>{{ user.email }}</p>
</div>
</div>
</div>
</template>
<script>
import { ref, onMounted, computed } from 'vue'
import { useCachedApi } from '@/composables/useCachedApi'
export default {
name: 'CachedDataDemo',
setup() {
const cacheStrategy = ref('cache-first')
const dataSource = ref('')
const loadTime = ref(0)
const {
data,
loading,
error,
execute,
clearCache,
getCacheStats
} = useCachedApi()
// 计算缓存统计信息
const cacheStats = computed(() => {
const stats = getCacheStats()
return `内存: ${stats.memory.size}项, 存储: ${stats.storage.keys}项`
})
// 加载数据
const loadData = async () => {
const startTime = Date.now()
try {
const result = await execute('/api/users', {
strategy: cacheStrategy.value,
memoryTTL: 2 * 60 * 1000, // 2分钟
storageTTL: 10 * 60 * 1000, // 10分钟
revalidateInBackground: true
})
dataSource.value = result.fromCache
? `缓存 (${result.cacheType})`
: '网络'
loadTime.value = Date.now() - startTime
} catch (err) {
console.error('加载数据失败:', err)
}
}
// 刷新数据
const refreshData = () => {
loadData()
}
// 清除缓存并重新加载
const handleClearCache = () => {
clearCache('/api/users')
loadData()
}
onMounted(() => {
loadData()
})
return {
data,
loading,
error,
cacheStrategy,
dataSource,
loadTime,
cacheStats,
refreshData,
clearCache: handleClearCache,
loadData
}
}
}
</script>
<style scoped>
.cached-data-demo {
padding: 1rem;
max-width: 800px;
margin: 0 auto;
}
.controls {
display: flex;
gap: 1rem;
margin-bottom: 1rem;
align-items: center;
}
.controls button {
padding: 0.5rem 1rem;
border: 1px solid #ddd;
border-radius: 4px;
background: white;
cursor: pointer;
}
.controls button:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.controls select {
padding: 0.5rem;
border: 1px solid #ddd;
border-radius: 4px;
}
.data-info {
background: #f5f5f5;
padding: 1rem;
border-radius: 4px;
margin-bottom: 1rem;
}
.data-info p {
margin: 0.25rem 0;
font-size: 0.9rem;
}
.error {
color: #e74c3c;
padding: 1rem;
background: #fdf2f2;
border-radius: 4px;
margin-bottom: 1rem;
}
.data-display {
border: 1px solid #ddd;
border-radius: 4px;
padding: 1rem;
}
.user-item {
border-bottom: 1px solid #eee;
padding: 0.5rem 0;
}
.user-item:last-child {
border-bottom: none;
}
.user-item h4 {
margin: 0 0 0.25rem 0;
}
.user-item p {
margin: 0;
color: #666;
font-size: 0.9rem;
}
</style>通过本节数据缓存策略的学习,你已经掌握:
A: 根据数据特性选择:静态数据用缓存优先,实时数据用网络优先,新闻类数据用过期重新验证。
A: 设置合理的最大容量、TTL时间,实现LRU淘汰策略,定期清理过期数据。
A: 使用版本号、时间戳或ETag,在数据更新时主动清除相关缓存。
A: 考虑存储空间限制、网络状况变化、电池消耗,优先使用内存缓存。
A: 添加缓存日志、提供缓存清除功能、使用开发者工具监控存储使用情况。
// 🎉 缓存策略选择指南
const cacheStrategyGuide = {
// 数据类型与策略映射
dataTypes: {
static: {
strategy: 'cache-first',
examples: ['配置信息', '字典数据', '静态资源'],
ttl: '长期 (1小时+)'
},
dynamic: {
strategy: 'network-first',
examples: ['用户状态', '实时数据', '交易信息'],
ttl: '短期 (5分钟内)'
},
news: {
strategy: 'stale-while-revalidate',
examples: ['新闻列表', '商品信息', '内容推荐'],
ttl: '中期 (15-30分钟)'
}
}
}// 🎉 缓存性能指标
const cacheMetrics = {
// 命中率指标
hitRate: {
memory: '内存缓存命中率',
storage: '存储缓存命中率',
overall: '总体缓存命中率'
},
// 性能指标
performance: {
loadTime: '数据加载时间',
cacheSize: '缓存占用空间',
networkSaved: '节省的网络请求'
}
}"数据缓存策略是现代Web应用性能优化的核心技术,合理的缓存设计能够显著提升用户体验和应用性能。恭喜你完成了第15章HTTP请求与数据交互的学习,你已经掌握了Vue项目中完整的数据交互技能体系!"