uni-app 数据缓存策略
章节介绍
数据缓存是移动应用开发中的重要技术,它可以显著提升应用性能、减少网络请求、降低服务器压力,同时在离线状态下也能提供基本功能。uni-app 提供了多种本地存储 API,支持不同场景的数据缓存需求。本章节将详细介绍 uni-app 中的数据缓存策略,包括本地存储 API 的使用、缓存机制的设计、缓存清理和更新策略等核心知识点,以及如何实现高效的数据缓存系统,帮助你构建性能更优的应用。
核心知识点讲解
1. 本地存储 API
uni-app 提供了多种本地存储 API,适用于不同的数据存储场景。
1. uni.setStorage / uni.setStorageSync
功能:将数据存储在本地缓存中,键名唯一。
参数:
key:存储的键名data:需要存储的数据success:接口调用成功的回调函数fail:接口调用失败的回调函数complete:接口调用结束的回调函数(无论成功或失败)
示例:
// 异步存储
uni.setStorage({
key: 'userInfo',
data: { name: '张三', age: 25 },
success: function() {
console.log('存储成功');
},
fail: function() {
console.error('存储失败');
}
});
// 同步存储
try {
uni.setStorageSync('userInfo', { name: '张三', age: 25 });
console.log('存储成功');
} catch (error) {
console.error('存储失败:', error);
}2. uni.getStorage / uni.getStorageSync
功能:从本地缓存中获取数据。
参数:
key:存储的键名success:接口调用成功的回调函数fail:接口调用失败的回调函数complete:接口调用结束的回调函数(无论成功或失败)
示例:
// 异步获取
uni.getStorage({
key: 'userInfo',
success: function(res) {
console.log('获取成功:', res.data);
},
fail: function() {
console.error('获取失败');
}
});
// 同步获取
try {
const userInfo = uni.getStorageSync('userInfo');
console.log('获取成功:', userInfo);
} catch (error) {
console.error('获取失败:', error);
}3. uni.removeStorage / uni.removeStorageSync
功能:从本地缓存中删除指定键名的数据。
参数:
key:存储的键名success:接口调用成功的回调函数fail:接口调用失败的回调函数complete:接口调用结束的回调函数(无论成功或失败)
示例:
// 异步删除
uni.removeStorage({
key: 'userInfo',
success: function() {
console.log('删除成功');
},
fail: function() {
console.error('删除失败');
}
});
// 同步删除
try {
uni.removeStorageSync('userInfo');
console.log('删除成功');
} catch (error) {
console.error('删除失败:', error);
}4. uni.clearStorage / uni.clearStorageSync
功能:清除本地缓存中的所有数据。
参数:
success:接口调用成功的回调函数fail:接口调用失败的回调函数complete:接口调用结束的回调函数(无论成功或失败)
示例:
// 异步清除
uni.clearStorage({
success: function() {
console.log('清除成功');
},
fail: function() {
console.error('清除失败');
}
});
// 同步清除
try {
uni.clearStorageSync();
console.log('清除成功');
} catch (error) {
console.error('清除失败:', error);
}5. uni.getStorageInfo / uni.getStorageInfoSync
功能:获取本地缓存的相关信息。
参数:
success:接口调用成功的回调函数fail:接口调用失败的回调函数complete:接口调用结束的回调函数(无论成功或失败)
返回值:
keys:本地缓存中的所有键名currentSize:当前占用的空间大小,单位:KBlimitSize:限制的空间大小,单位:KB
示例:
// 异步获取
uni.getStorageInfo({
success: function(res) {
console.log('所有键名:', res.keys);
console.log('当前占用空间:', res.currentSize);
console.log('限制空间:', res.limitSize);
},
fail: function() {
console.error('获取失败');
}
});
// 同步获取
try {
const storageInfo = uni.getStorageInfoSync();
console.log('所有键名:', storageInfo.keys);
console.log('当前占用空间:', storageInfo.currentSize);
console.log('限制空间:', storageInfo.limitSize);
} catch (error) {
console.error('获取失败:', error);
}2. 缓存机制
1. 缓存类型
根据缓存的生命周期和用途,可以将缓存分为以下几种类型:
- 临时缓存:应用运行期间的临时数据,不需要持久化存储
- 会话缓存:用户会话期间的数据,会话结束后可以清除
- 持久缓存:需要长期保存的数据,如用户配置、离线数据等
- 版本化缓存:与应用版本关联的缓存,版本更新时需要清理
2. 缓存策略
常见的缓存策略包括:
- **LRU (Least Recently Used)**:最近最少使用策略,当缓存空间不足时,删除最久未使用的数据
- **LFU (Least Frequently Used)**:最不经常使用策略,当缓存空间不足时,删除使用频率最低的数据
- **FIFO (First In First Out)**:先进先出策略,当缓存空间不足时,删除最早存入的数据
- **TTL (Time To Live)**:生存时间策略,为每个缓存项设置过期时间
3. 缓存工具类
为了更方便地使用本地存储 API,我们可以封装一个缓存工具类,提供更高级的缓存功能。
1. 基础缓存工具类
// utils/storage.js
/**
* 本地存储工具类
*/
export const storage = {
/**
* 存储数据
* @param {string} key - 存储键名
* @param {any} value - 存储值
* @param {number} expire - 过期时间(毫秒)
*/
set(key, value, expire = null) {
try {
const data = {
value,
expire: expire ? Date.now() + expire : null
};
uni.setStorageSync(key, data);
return true;
} catch (error) {
console.error('存储失败:', error);
return false;
}
},
/**
* 获取数据
* @param {string} key - 存储键名
* @returns {any} 存储的值
*/
get(key) {
try {
const data = uni.getStorageSync(key);
// 检查是否存在
if (data === undefined || data === null) {
return null;
}
// 检查是否过期
if (data.expire && Date.now() > data.expire) {
this.remove(key);
return null;
}
return data.value;
} catch (error) {
console.error('获取失败:', error);
return null;
}
},
/**
* 删除数据
* @param {string} key - 存储键名
*/
remove(key) {
try {
uni.removeStorageSync(key);
return true;
} catch (error) {
console.error('删除失败:', error);
return false;
}
},
/**
* 清除所有数据
*/
clear() {
try {
uni.clearStorageSync();
return true;
} catch (error) {
console.error('清除失败:', error);
return false;
}
},
/**
* 获取存储信息
* @returns {object} 存储信息
*/
getInfo() {
try {
return uni.getStorageInfoSync();
} catch (error) {
console.error('获取存储信息失败:', error);
return null;
}
},
/**
* 检查是否存在
* @param {string} key - 存储键名
* @returns {boolean} 是否存在
*/
has(key) {
try {
const value = this.get(key);
return value !== null;
} catch (error) {
return false;
}
}
};
export default storage;2. 高级缓存工具类
// utils/cache.js
import storage from './storage';
/**
* 缓存工具类
*/
export const cache = {
// 缓存键前缀
prefix: 'app_',
// 缓存版本
version: '1.0.0',
/**
* 生成带前缀的键名
* @param {string} key - 原始键名
* @returns {string} 带前缀的键名
*/
getKey(key) {
return `${this.prefix}${key}_${this.version}`;
},
/**
* 存储数据
* @param {string} key - 存储键名
* @param {any} value - 存储值
* @param {number} expire - 过期时间(毫秒)
*/
set(key, value, expire = null) {
return storage.set(this.getKey(key), value, expire);
},
/**
* 获取数据
* @param {string} key - 存储键名
* @returns {any} 存储的值
*/
get(key) {
return storage.get(this.getKey(key));
},
/**
* 删除数据
* @param {string} key - 存储键名
*/
remove(key) {
return storage.remove(this.getKey(key));
},
/**
* 清除所有数据
*/
clear() {
return storage.clear();
},
/**
* 清除指定前缀的数据
* @param {string} prefix - 前缀
*/
clearByPrefix(prefix) {
try {
const info = storage.getInfo();
if (info && info.keys) {
info.keys.forEach(key => {
if (key.startsWith(`${this.prefix}${prefix}`)) {
storage.remove(key);
}
});
}
return true;
} catch (error) {
console.error('清除失败:', error);
return false;
}
},
/**
* 缓存网络请求数据
* @param {string} key - 缓存键名
* @param {function} requestFn - 请求函数
* @param {number} expire - 过期时间(毫秒)
* @returns {Promise} 请求结果
*/
async cachedRequest(key, requestFn, expire = 5 * 60 * 1000) {
// 尝试从缓存获取
const cachedData = this.get(key);
if (cachedData) {
return cachedData;
}
// 发起请求
try {
const data = await requestFn();
// 缓存数据
this.set(key, data, expire);
return data;
} catch (error) {
throw error;
}
}
};
export default cache;4. 缓存策略实现
1. 数据缓存策略
// utils/cacheStrategy.js
import cache from './cache';
/**
* 缓存策略
*/
export const cacheStrategy = {
/**
* 缓存配置
*/
config: {
// 不同类型数据的过期时间
expire: {
// 用户信息:1小时
user: 60 * 60 * 1000,
// 商品列表:5分钟
productList: 5 * 60 * 1000,
// 商品详情:30分钟
productDetail: 30 * 60 * 1000,
// 配置信息:1天
config: 24 * 60 * 60 * 1000
}
},
/**
* 缓存用户信息
* @param {object} userInfo - 用户信息
*/
cacheUserInfo(userInfo) {
return cache.set('userInfo', userInfo, this.config.expire.user);
},
/**
* 获取用户信息
* @returns {object} 用户信息
*/
getUserInfo() {
return cache.get('userInfo');
},
/**
* 缓存商品列表
* @param {array} productList - 商品列表
* @param {object} params - 请求参数
*/
cacheProductList(productList, params) {
const key = `productList_${JSON.stringify(params)}`;
return cache.set(key, productList, this.config.expire.productList);
},
/**
* 获取商品列表
* @param {object} params - 请求参数
* @returns {array} 商品列表
*/
getProductList(params) {
const key = `productList_${JSON.stringify(params)}`;
return cache.get(key);
},
/**
* 缓存商品详情
* @param {object} productDetail - 商品详情
* @param {string} productId - 商品ID
*/
cacheProductDetail(productDetail, productId) {
const key = `productDetail_${productId}`;
return cache.set(key, productDetail, this.config.expire.productDetail);
},
/**
* 获取商品详情
* @param {string} productId - 商品ID
* @returns {object} 商品详情
*/
getProductDetail(productId) {
const key = `productDetail_${productId}`;
return cache.get(key);
},
/**
* 清除商品相关缓存
*/
clearProductCache() {
return cache.clearByPrefix('product');
},
/**
* 清除用户相关缓存
*/
clearUserCache() {
return cache.clearByPrefix('user');
},
/**
* 清除所有缓存
*/
clearAllCache() {
return cache.clear();
}
};
export default cacheStrategy;2. 网络请求缓存
// utils/requestWithCache.js
import request from './request';
import cache from './cache';
/**
* 带缓存的网络请求
* @param {string} url - 请求地址
* @param {object} options - 请求选项
* @param {number} expire - 缓存过期时间(毫秒)
* @returns {Promise} 请求结果
*/
export const requestWithCache = async (url, options = {}, expire = 5 * 60 * 1000) => {
// 生成缓存键
const cacheKey = `request_${url}_${JSON.stringify(options)}`;
// 尝试从缓存获取
const cachedData = cache.get(cacheKey);
if (cachedData) {
return cachedData;
}
// 发起请求
try {
const data = await request({
url,
...options
});
// 缓存数据
cache.set(cacheKey, data, expire);
return data;
} catch (error) {
throw error;
}
};
export default requestWithCache;5. 缓存管理
1. 缓存监控
// utils/cacheMonitor.js
import storage from './storage';
/**
* 缓存监控
*/
export const cacheMonitor = {
/**
* 检查缓存状态
*/
checkCacheStatus() {
try {
const info = storage.getInfo();
if (info) {
console.log('缓存状态:', {
keys: info.keys.length,
currentSize: info.currentSize,
limitSize: info.limitSize,
usage: ((info.currentSize / info.limitSize) * 100).toFixed(2) + '%'
});
// 检查缓存是否接近上限
if (info.currentSize / info.limitSize > 0.8) {
console.warn('缓存空间即将用完,请考虑清理缓存');
}
}
} catch (error) {
console.error('检查缓存状态失败:', error);
}
},
/**
* 清理过期缓存
*/
clearExpiredCache() {
try {
const info = storage.getInfo();
if (info && info.keys) {
let clearedCount = 0;
info.keys.forEach(key => {
try {
const data = uni.getStorageSync(key);
if (data && data.expire && Date.now() > data.expire) {
uni.removeStorageSync(key);
clearedCount++;
}
} catch (error) {
console.error(`检查缓存项 ${key} 失败:`, error);
}
});
console.log(`清理了 ${clearedCount} 个过期缓存`);
}
} catch (error) {
console.error('清理过期缓存失败:', error);
}
}
};
export default cacheMonitor;2. 缓存清理
// utils/cacheCleaner.js
import storage from './storage';
import cache from './cache';
/**
* 缓存清理器
*/
export const cacheCleaner = {
/**
* 清理指定类型的缓存
* @param {string} type - 缓存类型
*/
clearCacheByType(type) {
switch (type) {
case 'all':
return this.clearAllCache();
case 'user':
return cache.clearByPrefix('user');
case 'product':
return cache.clearByPrefix('product');
case 'image':
return this.clearImageCache();
default:
return false;
}
},
/**
* 清理所有缓存
*/
clearAllCache() {
return cache.clear();
},
/**
* 清理图片缓存
*/
clearImageCache() {
// #ifdef APP-PLUS
uni.clearImageCache({
success: function() {
console.log('图片缓存清理成功');
},
fail: function() {
console.error('图片缓存清理失败');
}
});
// #endif
return true;
},
/**
* 获取缓存大小
* @returns {Promise<number>} 缓存大小(MB)
*/
getCacheSize() {
return new Promise((resolve) => {
// #ifdef APP-PLUS
plus.cache.calculate((size) => {
const cacheSize = (size / (1024 * 1024)).toFixed(2);
resolve(parseFloat(cacheSize));
});
// #endif
// #ifdef H5
const info = storage.getInfo();
const cacheSize = (info.currentSize / 1024).toFixed(2);
resolve(parseFloat(cacheSize));
// #endif
// #ifdef MP-WEIXIN
const info = storage.getInfo();
const cacheSize = (info.currentSize / 1024).toFixed(2);
resolve(parseFloat(cacheSize));
// #endif
});
}
};
export default cacheCleaner;实用案例分析
案例:实现数据缓存系统
1. 缓存工具类实现
// utils/storage.js
/**
* 本地存储工具类
*/
export const storage = {
/**
* 存储数据
* @param {string} key - 存储键名
* @param {any} value - 存储值
* @param {number} expire - 过期时间(毫秒)
*/
set(key, value, expire = null) {
try {
const data = {
value,
expire: expire ? Date.now() + expire : null
};
uni.setStorageSync(key, data);
return true;
} catch (error) {
console.error('存储失败:', error);
return false;
}
},
/**
* 获取数据
* @param {string} key - 存储键名
* @returns {any} 存储的值
*/
get(key) {
try {
const data = uni.getStorageSync(key);
// 检查是否存在
if (data === undefined || data === null) {
return null;
}
// 检查是否过期
if (data.expire && Date.now() > data.expire) {
this.remove(key);
return null;
}
return data.value;
} catch (error) {
console.error('获取失败:', error);
return null;
}
},
/**
* 删除数据
* @param {string} key - 存储键名
*/
remove(key) {
try {
uni.removeStorageSync(key);
return true;
} catch (error) {
console.error('删除失败:', error);
return false;
}
},
/**
* 清除所有数据
*/
clear() {
try {
uni.clearStorageSync();
return true;
} catch (error) {
console.error('清除失败:', error);
return false;
}
},
/**
* 获取存储信息
* @returns {object} 存储信息
*/
getInfo() {
try {
return uni.getStorageInfoSync();
} catch (error) {
console.error('获取存储信息失败:', error);
return null;
}
},
/**
* 检查是否存在
* @param {string} key - 存储键名
* @returns {boolean} 是否存在
*/
has(key) {
try {
const value = this.get(key);
return value !== null;
} catch (error) {
return false;
}
}
};
export default storage;// utils/cache.js
import storage from './storage';
/**
* 缓存工具类
*/
export const cache = {
// 缓存键前缀
prefix: 'app_',
// 缓存版本
version: '1.0.0',
/**
* 生成带前缀的键名
* @param {string} key - 原始键名
* @returns {string} 带前缀的键名
*/
getKey(key) {
return `${this.prefix}${key}_${this.version}`;
},
/**
* 存储数据
* @param {string} key - 存储键名
* @param {any} value - 存储值
* @param {number} expire - 过期时间(毫秒)
*/
set(key, value, expire = null) {
return storage.set(this.getKey(key), value, expire);
},
/**
* 获取数据
* @param {string} key - 存储键名
* @returns {any} 存储的值
*/
get(key) {
return storage.get(this.getKey(key));
},
/**
* 删除数据
* @param {string} key - 存储键名
*/
remove(key) {
return storage.remove(this.getKey(key));
},
/**
* 清除所有数据
*/
clear() {
return storage.clear();
},
/**
* 清除指定前缀的数据
* @param {string} prefix - 前缀
*/
clearByPrefix(prefix) {
try {
const info = storage.getInfo();
if (info && info.keys) {
info.keys.forEach(key => {
if (key.startsWith(`${this.prefix}${prefix}`)) {
storage.remove(key);
}
});
}
return true;
} catch (error) {
console.error('清除失败:', error);
return false;
}
},
/**
* 缓存网络请求数据
* @param {string} key - 缓存键名
* @param {function} requestFn - 请求函数
* @param {number} expire - 过期时间(毫秒)
* @returns {Promise} 请求结果
*/
async cachedRequest(key, requestFn, expire = 5 * 60 * 1000) {
// 尝试从缓存获取
const cachedData = this.get(key);
if (cachedData) {
return cachedData;
}
// 发起请求
try {
const data = await requestFn();
// 缓存数据
this.set(key, data, expire);
return data;
} catch (error) {
throw error;
}
}
};
export default cache;2. 缓存策略实现
// utils/cacheStrategy.js
import cache from './cache';
/**
* 缓存策略
*/
export const cacheStrategy = {
/**
* 缓存配置
*/
config: {
// 不同类型数据的过期时间
expire: {
// 用户信息:1小时
user: 60 * 60 * 1000,
// 商品列表:5分钟
productList: 5 * 60 * 1000,
// 商品详情:30分钟
productDetail: 30 * 60 * 1000,
// 配置信息:1天
config: 24 * 60 * 60 * 1000
}
},
/**
* 缓存用户信息
* @param {object} userInfo - 用户信息
*/
cacheUserInfo(userInfo) {
return cache.set('userInfo', userInfo, this.config.expire.user);
},
/**
* 获取用户信息
* @returns {object} 用户信息
*/
getUserInfo() {
return cache.get('userInfo');
},
/**
* 缓存商品列表
* @param {array} productList - 商品列表
* @param {object} params - 请求参数
*/
cacheProductList(productList, params) {
const key = `productList_${JSON.stringify(params)}`;
return cache.set(key, productList, this.config.expire.productList);
},
/**
* 获取商品列表
* @param {object} params - 请求参数
* @returns {array} 商品列表
*/
getProductList(params) {
const key = `productList_${JSON.stringify(params)}`;
return cache.get(key);
},
/**
* 缓存商品详情
* @param {object} productDetail - 商品详情
* @param {string} productId - 商品ID
*/
cacheProductDetail(productDetail, productId) {
const key = `productDetail_${productId}`;
return cache.set(key, productDetail, this.config.expire.productDetail);
},
/**
* 获取商品详情
* @param {string} productId - 商品ID
* @returns {object} 商品详情
*/
getProductDetail(productId) {
const key = `productDetail_${productId}`;
return cache.get(key);
},
/**
* 清除商品相关缓存
*/
clearProductCache() {
return cache.clearByPrefix('product');
},
/**
* 清除用户相关缓存
*/
clearUserCache() {
return cache.clearByPrefix('user');
},
/**
* 清除所有缓存
*/
clearAllCache() {
return cache.clear();
}
};
export default cacheStrategy;3. 缓存管理实现
// utils/cacheManager.js
import cache from './cache';
import storage from './storage';
/**
* 缓存管理器
*/
export const cacheManager = {
/**
* 初始化缓存
*/
init() {
// 检查缓存版本
this.checkVersion();
// 清理过期缓存
this.cleanExpiredCache();
},
/**
* 检查缓存版本
*/
checkVersion() {
const currentVersion = cache.version;
const cachedVersion = storage.get('appVersion');
if (cachedVersion !== currentVersion) {
// 版本更新,清理旧缓存
console.log('缓存版本更新,清理旧缓存');
cache.clear();
storage.set('appVersion', currentVersion);
}
},
/**
* 清理过期缓存
*/
cleanExpiredCache() {
try {
const info = storage.getInfo();
if (info && info.keys) {
let clearedCount = 0;
info.keys.forEach(key => {
try {
const data = storage.get(key);
if (data === null) {
storage.remove(key);
clearedCount++;
}
} catch (error) {
console.error(`检查缓存项 ${key} 失败:`, error);
}
});
if (clearedCount > 0) {
console.log(`清理了 ${clearedCount} 个过期缓存`);
}
}
} catch (error) {
console.error('清理过期缓存失败:', error);
}
},
/**
* 监控缓存状态
*/
monitorCacheStatus() {
const info = storage.getInfo();
if (info) {
const usage = (info.currentSize / info.limitSize) * 100;
console.log(`缓存使用情况: ${info.currentSize}KB / ${info.limitSize}KB (${usage.toFixed(2)}%)`);
if (usage > 80) {
console.warn('缓存空间即将用完,请考虑清理缓存');
}
}
}
};
export default cacheManager;4. 实际应用示例
<!-- pages/product/list.vue -->
<template>
<view class="container">
<view class="product-list">
<view
v-for="product in productList"
:key="product.id"
class="product-item"
@click="navigateToDetail(product.id)"
>
<image :src="product.image" class="product-image"></image>
<view class="product-info">
<text class="product-name">{{ product.name }}</text>
<text class="product-price">¥{{ product.price }}</text>
</view>
</view>
</view>
</view>
</template>
<script>
import api from '@/api';
import { cacheStrategy } from '@/utils/cacheStrategy';
import { cacheManager } from '@/utils/cacheManager';
export default {
data() {
return {
productList: [],
page: 1,
pageSize: 10
};
},
onLoad() {
// 初始化缓存管理器
cacheManager.init();
// 监控缓存状态
cacheManager.monitorCacheStatus();
this.getProductList();
},
methods: {
async getProductList() {
const params = {
page: this.page,
pageSize: this.pageSize
};
// 尝试从缓存获取
const cachedList = cacheStrategy.getProductList(params);
if (cachedList) {
console.log('从缓存获取商品列表');
this.productList = cachedList;
return;
}
// 从服务器获取
try {
console.log('从服务器获取商品列表');
const res = await api.product.getProductList(params);
this.productList = res.data.list;
// 缓存数据
cacheStrategy.cacheProductList(this.productList, params);
} catch (error) {
console.error('获取商品列表失败:', error);
}
},
navigateToDetail(productId) {
uni.navigateTo({
url: `/pages/product/detail?id=${productId}`
});
}
}
};
</script>
<style scoped>
.container {
flex: 1;
padding: 20rpx;
background-color: #f5f5f5;
}
.product-list {
display: flex;
flex-direction: column;
gap: 20rpx;
}
.product-item {
display: flex;
padding: 20rpx;
background-color: #fff;
border-radius: 10rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
}
.product-image {
width: 160rpx;
height: 160rpx;
border-radius: 10rpx;
margin-right: 20rpx;
}
.product-info {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.product-name {
font-size: 28rpx;
color: #333;
margin-bottom: 10rpx;
}
.product-price {
font-size: 32rpx;
color: #ff4d4f;
font-weight: bold;
}
</style><!-- pages/product/detail.vue -->
<template>
<view class="container">
<view v-if="productDetail">
<image :src="productDetail.image" class="product-image"></image>
<view class="product-info">
<text class="product-name">{{ productDetail.name }}</text>
<text class="product-price">¥{{ productDetail.price }}</text>
<text class="product-desc">{{ productDetail.description }}</text>
</view>
</view>
<view v-else class="loading">加载中...</view>
</view>
</template>
<script>
import api from '@/api';
import { cacheStrategy } from '@/utils/cacheStrategy';
export default {
data() {
return {
productDetail: null,
productId: ''
};
},
onLoad(options) {
this.productId = options.id;
this.getProductDetail();
},
methods: {
async getProductDetail() {
// 尝试从缓存获取
const cachedDetail = cacheStrategy.getProductDetail(this.productId);
if (cachedDetail) {
console.log('从缓存获取商品详情');
this.productDetail = cachedDetail;
return;
}
// 从服务器获取
try {
console.log('从服务器获取商品详情');
const res = await api.product.getProductDetail(this.productId);
this.productDetail = res.data;
// 缓存数据
cacheStrategy.cacheProductDetail(this.productDetail, this.productId);
} catch (error) {
console.error('获取商品详情失败:', error);
}
}
}
};
</script>
<style scoped>
.container {
flex: 1;
padding: 20rpx;
background-color: #f5f5f5;
}
.product-image {
width: 100%;
height: 400rpx;
border-radius: 10rpx;
margin-bottom: 20rpx;
}
.product-info {
background-color: #fff;
padding: 20rpx;
border-radius: 10rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
}
.product-name {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 10rpx;
display: block;
}
.product-price {
font-size: 36rpx;
color: #ff4d4f;
font-weight: bold;
margin-bottom: 20rpx;
display: block;
}
.product-desc {
font-size: 24rpx;
color: #666;
line-height: 1.5;
}
.loading {
text-align: center;
padding: 100rpx 0;
color: #999;
}
</style><!-- pages/settings/settings.vue -->
<template>
<view class="container">
<view class="setting-item" @click="handleClearCache">
<text class="setting-label">清除缓存</text>
<view class="setting-right">
<text class="cache-size">{{ cacheSize }}MB</text>
<text class="arrow">></text>
</view>
</view>
<view class="setting-item" @click="handleClearTypeCache">
<text class="setting-label">清除类型缓存</text>
<text class="arrow">></text>
</view>
</view>
</template>
<script>
import { cacheCleaner } from '@/utils/cacheCleaner';
export default {
data() {
return {
cacheSize: 0
};
},
onLoad() {
this.getCacheSize();
},
methods: {
async getCacheSize() {
const size = await cacheCleaner.getCacheSize();
this.cacheSize = size;
},
handleClearCache() {
uni.showModal({
title: '清除缓存',
content: '确定要清除所有缓存吗?',
success: (res) => {
if (res.confirm) {
cacheCleaner.clearAllCache();
this.getCacheSize();
uni.showToast({
title: '缓存清除成功',
icon: 'success'
});
}
}
});
},
handleClearTypeCache() {
uni.showActionSheet({
itemList: ['清除用户缓存', '清除商品缓存', '清除图片缓存'],
success: (res) => {
let type = '';
switch (res.tapIndex) {
case 0:
type = 'user';
break;
case 1:
type = 'product';
break;
case 2:
type = 'image';
break;
}
cacheCleaner.clearCacheByType(type);
this.getCacheSize();
uni.showToast({
title: '缓存清除成功',
icon: 'success'
});
}
});
}
}
};
</script>
<style scoped>
.container {
flex: 1;
background-color: #f5f5f5;
}
.setting-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx 20rpx;
background-color: #fff;
margin-bottom: 1rpx;
}
.setting-label {
font-size: 28rpx;
color: #333;
}
.setting-right {
display: flex;
align-items: center;
}
.cache-size {
font-size: 24rpx;
color: #999;
margin-right: 10rpx;
}
.arrow {
font-size: 24rpx;
color: #999;
}
</style>常见问题与解决方案
1. 缓存空间不足
问题:本地缓存空间不足,导致无法存储新数据。
解决方案:
- 定期清理过期缓存
- 对不同类型的数据设置合理的过期时间
- 监控缓存使用情况,在接近上限时提醒用户清理
- 使用版本化缓存,在应用版本更新时清理旧缓存
2. 缓存数据过期
问题:缓存数据过期,导致显示旧数据或错误数据。
解决方案:
- 为缓存数据设置合理的过期时间
- 在获取缓存数据时检查是否过期
- 实现缓存更新机制,确保数据及时更新
- 对于重要数据,每次都从服务器获取并更新缓存
3. 缓存一致性
问题:缓存数据与服务器数据不一致,导致显示错误信息。
解决方案:
- 实现缓存更新策略,如轮询、WebSocket 推送等
- 在数据发生变化时主动更新缓存
- 对于关键操作,先更新服务器数据,再更新缓存
- 使用乐观锁或悲观锁确保数据一致性
4. 缓存清理不彻底
问题:缓存清理后,仍有部分缓存残留。
解决方案:
- 实现全面的缓存清理功能,包括本地存储、图片缓存等
- 针对不同类型的缓存采用不同的清理策略
- 提供缓存清理进度和结果反馈
- 在应用版本更新时强制清理所有旧缓存
5. 缓存性能问题
问题:缓存操作影响应用性能,如存储大量数据时卡顿。
解决方案:
- 对于大量数据,使用异步存储 API
- 实现缓存批处理,减少频繁的存储操作
- 对缓存数据进行压缩,减少存储空间和读写时间
- 使用索引和分页技术,提高缓存查询效率
代码优化建议
1. 优化存储操作
// 优化前:频繁调用存储 API
for (let i = 0; i < 100; i++) {
uni.setStorageSync(`key_${i}`, i);
}
// 优化后:批量存储
const data = {};
for (let i = 0; i < 100; i++) {
data[`key_${i}`] = i;
}
uni.setStorageSync('batchData', data);2. 优化缓存键生成
// 优化前:复杂的键名生成
const key = `productList_${params.page}_${params.pageSize}_${params.category}`;
// 优化后:使用 JSON.stringify 生成键名
const key = `productList_${JSON.stringify(params)}`;3. 优化缓存检查
// 优化前:每次都检查缓存
async function getData(params) {
const cachedData = cache.get(`data_${JSON.stringify(params)}`);
if (cachedData) {
return cachedData;
}
const data = await fetchData(params);
cache.set(`data_${JSON.stringify(params)}`, data);
return data;
}
// 优化后:使用缓存请求封装
async function getData(params) {
return cache.cachedRequest(`data_${JSON.stringify(params)}`, () => fetchData(params));
}4. 优化缓存策略
// 优化前:所有数据使用相同的过期时间
cache.set('productList', data, 5 * 60 * 1000);
cache.set('userInfo', data, 5 * 60 * 1000);
// 优化后:根据数据类型设置不同的过期时间
cache.set('productList', data, 5 * 60 * 1000);
cache.set('userInfo', data, 60 * 60 * 1000);5. 优化缓存清理
// 优化前:手动清理每个缓存项
uni.removeStorageSync('userInfo');
uni.removeStorageSync('productList');
uni.removeStorageSync('productDetail');
// 优化后:按前缀清理
cache.clearByPrefix('user');
cache.clearByPrefix('product');章节总结
本章节详细介绍了 uni-app 中的数据缓存策略,包括:
- 本地存储 API:介绍了 uni-app 提供的本地存储 API,如 setStorage、getStorage、removeStorage 等
- 缓存工具类:封装了基础缓存工具类和高级缓存工具类,提供更方便的缓存操作
- 缓存策略:实现了不同类型数据的缓存策略,包括过期时间设置、缓存键生成等
- 缓存管理:实现了缓存监控、缓存清理等功能,确保缓存系统的正常运行
- 实用案例:通过商品列表、商品详情和设置页面的示例,展示了缓存系统的实际应用
- 常见问题与解决方案:针对缓存空间不足、数据过期、一致性等问题提供了解决方案
- 代码优化建议:提供了缓存操作、键生成、检查和清理等方面的优化建议
通过本章节的学习,你应该能够掌握 uni-app 中的数据缓存策略,构建高效、可靠的缓存系统,提升应用性能和用户体验。在实际开发中,应根据应用的具体需求,选择合适的缓存策略和工具,确保数据的准确性和系统的稳定性。