1. uni-app网络请求与数据缓存核心概念
在移动应用开发中,网络请求和数据缓存是构建现代应用的两大基石。uni-app作为跨平台开发框架,其网络请求和数据缓存机制有着独特的设计理念和实现方式。我们先来看一个典型的应用场景:当用户打开一个新闻类APP时,首先会通过网络请求获取最新的新闻列表,同时将部分数据缓存在本地,这样即使在网络不稳定的情况下,用户仍然能够浏览之前加载过的内容。
uni-app提供了两种主要的网络请求方式:
- 传统ajax请求:通过uni.request API实现
- 封装后的请求库:如uniCloud.httpclient等
数据缓存方面则主要分为:
- 临时缓存:应用运行时存储在内存中的数据
- 持久化缓存:使用本地存储技术保存的数据
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 网络请求深度解析
2.1 uni.request基础使用
uni.request是uni-app中最基础的网络请求API,其基本用法如下:
javascript复制uni.request({
url: 'https://example.com/api/news',
method: 'GET',
data: {
page: 1,
size: 10
},
success: (res) => {
console.log(res.data);
},
fail: (err) => {
console.error(err);
}
});
关键参数说明:
- url:请求地址(必需)
- method:请求方法(GET/POST等)
- data:发送的数据
- header:请求头设置
- success:成功回调
- fail:失败回调
实际开发中发现,很多开发者容易忽略header的设置,导致接口认证失败。建议在项目初期就统一封装请求头处理逻辑。
2.2 请求封装最佳实践
直接使用uni.request虽然简单,但在实际项目中我们通常会进行二次封装。以下是推荐的封装方案:
javascript复制const http = {
baseURL: 'https://api.example.com',
request(options) {
return new Promise((resolve, reject) => {
uni.request({
url: this.baseURL + options.url,
method: options.method || 'GET',
data: options.data || {},
header: {
'Content-Type': 'application/json',
'Authorization': uni.getStorageSync('token') || ''
...options.header
},
success: (res) => {
if (res.statusCode === 200) {
resolve(res.data);
} else {
reject(res);
}
},
fail: (err) => {
reject(err);
}
});
});
}
};
// 使用示例
http.request({
url: '/news/list',
method: 'GET'
}).then(data => {
console.log(data);
}).catch(err => {
console.error(err);
});
这种封装方式带来了几个优势:
- 统一处理baseURL,避免硬编码
- 自动添加认证token
- 使用Promise风格,便于异步处理
- 统一错误处理机制
2.3 高级请求技巧
2.3.1 请求拦截与响应拦截
在实际项目中,我们经常需要在请求发出前或响应返回后进行统一处理。下面是一个拦截器实现方案:
javascript复制const http = {
// 请求拦截器
requestInterceptors: [],
// 响应拦截器
responseInterceptors: [],
addRequestInterceptor(interceptor) {
this.requestInterceptors.push(interceptor);
},
addResponseInterceptor(interceptor) {
this.responseInterceptors.push(interceptor);
},
async request(options) {
// 执行请求拦截
for (const interceptor of this.requestInterceptors) {
options = await interceptor(options) || options;
}
const response = await new Promise((resolve, reject) => {
uni.request({
...options,
success: resolve,
fail: reject
});
});
let processedResponse = response;
// 执行响应拦截
for (const interceptor of this.responseInterceptors) {
processedResponse = await interceptor(processedResponse) || processedResponse;
}
return processedResponse;
}
};
// 添加请求拦截器 - 示例:添加loading
http.addRequestInterceptor((options) => {
uni.showLoading({ title: '加载中...' });
return options;
});
// 添加响应拦截器 - 示例:隐藏loading
http.addResponseInterceptor((response) => {
uni.hideLoading();
return response;
});
2.3.2 并发请求处理
当需要同时发送多个请求时,可以使用Promise.all:
javascript复制const fetchUser = http.request({ url: '/user/info' });
const fetchNews = http.request({ url: '/news/list' });
Promise.all([fetchUser, fetchNews])
.then(([userData, newsData]) => {
console.log('用户数据:', userData);
console.log('新闻数据:', newsData);
})
.catch(err => {
console.error('请求失败:', err);
});
2.3.3 请求取消机制
在某些场景下(如页面卸载时),我们需要取消未完成的请求。uni-app虽然没有原生提供取消API,但可以通过以下方式实现:
javascript复制let requestTask = null;
// 发起请求
requestTask = uni.request({
url: 'https://example.com/api',
success() {
requestTask = null;
}
});
// 取消请求
if (requestTask) {
requestTask.abort();
requestTask = null;
}
3. 数据缓存全面指南
3.1 本地存储基础
uni-app提供了多种数据存储方案:
-
uni.setStorage / uni.getStorage
- 异步存储,适合大多数场景
- 存储上限约10MB
- 持久化存储,应用关闭后仍然存在
-
uni.setStorageSync / uni.getStorageSync
- 同步存储,简单场景使用
- 同样有10MB限制
-
全局变量
- 仅在应用运行时有效
- 适合临时数据存储
基础使用示例:
javascript复制// 异步存储
uni.setStorage({
key: 'userInfo',
data: { name: '张三', age: 25 },
success() {
console.log('存储成功');
}
});
// 同步存储
try {
uni.setStorageSync('settings', { theme: 'dark' });
} catch (e) {
console.error('存储失败', e);
}
// 读取数据
const userInfo = uni.getStorageSync('userInfo');
console.log(userInfo);
3.2 缓存策略设计
合理的缓存策略可以显著提升应用性能。以下是几种常见策略:
3.2.1 网络优先策略
javascript复制async function fetchWithCache(url, cacheKey) {
try {
// 先尝试网络请求
const freshData = await http.request({ url });
// 请求成功后更新缓存
uni.setStorageSync(cacheKey, {
data: freshData,
timestamp: Date.now()
});
return freshData;
} catch (err) {
// 网络请求失败时尝试读取缓存
const cached = uni.getStorageSync(cacheKey);
if (cached) {
return cached.data;
}
throw err;
}
}
3.2.2 缓存优先策略
javascript复制async function fetchWithCacheFirst(url, cacheKey, maxAge = 3600000) {
// 先尝试读取缓存
const cached = uni.getStorageSync(cacheKey);
if (cached && (Date.now() - cached.timestamp) < maxAge) {
return cached.data;
}
// 缓存不存在或过期,发起网络请求
try {
const freshData = await http.request({ url });
uni.setStorageSync(cacheKey, {
data: freshData,
timestamp: Date.now()
});
return freshData;
} catch (err) {
// 网络请求失败时,如果有缓存且允许使用过期数据,则返回缓存
if (cached) {
return cached.data;
}
throw err;
}
}
3.3 高级缓存技巧
3.3.1 缓存自动清理
长期使用后,缓存可能占用过多空间。我们可以实现自动清理机制:
javascript复制function cleanOldCache(maxCount = 50) {
const allKeys = uni.getStorageInfoSync().keys;
if (allKeys.length <= maxCount) return;
// 获取所有缓存项并排序(按时间戳)
const cacheItems = allKeys.map(key => {
const item = uni.getStorageSync(key);
return { key, timestamp: item.timestamp || 0 };
}).sort((a, b) => a.timestamp - b.timestamp);
// 删除最早的缓存项
const toDelete = cacheItems.slice(0, cacheItems.length - maxCount);
toDelete.forEach(item => {
uni.removeStorageSync(item.key);
});
}
3.3.2 缓存加密
对于敏感数据,建议进行加密存储:
javascript复制// 简单加密示例(实际项目中应使用更安全的加密算法)
function encrypt(data, key = 'secret') {
return JSON.stringify(data).split('').map(c =>
String.fromCharCode(c.charCodeAt(0) ^ key.charCodeAt(0))
).join('');
}
function decrypt(encrypted, key = 'secret') {
const str = encrypted.split('').map(c =>
String.fromCharCode(c.charCodeAt(0) ^ key.charCodeAt(0))
).join('');
return JSON.parse(str);
}
// 使用示例
const sensitiveData = { token: 'abc123' };
const encrypted = encrypt(sensitiveData);
uni.setStorageSync('auth', encrypted);
const decrypted = decrypt(uni.getStorageSync('auth'));
console.log(decrypted);
4. 网络与缓存实战应用
4.1 新闻列表页实现
结合网络请求和缓存的典型场景:
javascript复制// pages/news/list.vue
export default {
data() {
return {
newsList: [],
loading: false,
error: null
};
},
async onLoad() {
await this.loadNews();
},
methods: {
async loadNews() {
this.loading = true;
this.error = null;
try {
// 尝试从缓存读取
const cached = uni.getStorageSync('newsList');
if (cached) {
this.newsList = cached.data;
}
// 同时发起网络请求
const freshData = await http.request({
url: '/news/list',
method: 'GET'
});
// 更新数据和缓存
this.newsList = freshData;
uni.setStorageSync('newsList', {
data: freshData,
timestamp: Date.now()
});
} catch (err) {
this.error = err.message;
if (!this.newsList.length) {
uni.showToast({
title: '加载失败,请检查网络',
icon: 'none'
});
}
} finally {
this.loading = false;
}
},
refresh() {
// 强制刷新,忽略缓存
uni.removeStorageSync('newsList');
this.loadNews();
}
}
};
4.2 图片缓存优化
对于图片资源,我们可以实现更智能的缓存策略:
javascript复制// utils/imageCache.js
const imageCache = {
async getImage(url) {
// 检查是否有缓存
const cacheKey = `image_${md5(url)}`;
const cached = uni.getStorageSync(cacheKey);
if (cached) {
return cached; // 返回base64格式的缓存图片
}
// 无缓存,下载图片
try {
const [err, res] = await uni.downloadFile({ url });
if (err) throw err;
// 将图片转为base64缓存
const base64 = await this.fileToBase64(res.tempFilePath);
uni.setStorageSync(cacheKey, base64);
return base64;
} catch (err) {
console.error('图片下载失败:', err);
return url; // 失败时返回原始URL
}
},
fileToBase64(tempFilePath) {
return new Promise((resolve) => {
uni.getFileSystemManager().readFile({
filePath: tempFilePath,
encoding: 'base64',
success: (res) => {
resolve(`data:image/png;base64,${res.data}`);
},
fail: () => {
resolve(null);
}
});
});
}
};
// 使用示例
const cachedImage = await imageCache.getImage('https://example.com/image.jpg');
this.imageSrc = cachedImage;
4.3 离线模式实现
通过Service Worker和缓存机制实现离线功能:
javascript复制// 注册Service Worker
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js').then(registration => {
console.log('SW注册成功:', registration);
}).catch(err => {
console.log('SW注册失败:', err);
});
}
// sw.js - Service Worker脚本
const CACHE_NAME = 'my-app-cache-v1';
const urlsToCache = [
'/',
'/static/css/app.css',
'/static/js/app.js',
'/api/data?cache=1'
];
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => cache.addAll(urlsToCache))
);
});
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
.then(response => {
// 缓存命中则返回缓存,否则发起网络请求
return response || fetch(event.request);
})
);
});
5. 性能优化与调试技巧
5.1 网络请求优化
-
合并请求:将多个小请求合并为一个批量请求
javascript复制// 不好的做法 async function fetchAllData() { const user = await fetchUser(); const posts = await fetchPosts(); const comments = await fetchComments(); return { user, posts, comments }; } // 优化后的做法 async function fetchAllData() { const [user, posts, comments] = await Promise.all([ fetchUser(), fetchPosts(), fetchComments() ]); return { user, posts, comments }; } -
数据压缩:开启gzip压缩,减少传输数据量
javascript复制// 在请求头中声明支持压缩 headers: { 'Accept-Encoding': 'gzip, deflate' } -
使用HTTP/2:利用多路复用特性提升性能
5.2 缓存性能优化
-
分片缓存:大数据拆分为多个小缓存
javascript复制function saveLargeData(key, data, chunkSize = 102400) { const chunks = []; for (let i = 0; i < data.length; i += chunkSize) { chunks.push(data.slice(i, i + chunkSize)); } uni.setStorageSync(`${key}_count`, chunks.length); chunks.forEach((chunk, index) => { uni.setStorageSync(`${key}_${index}`, chunk); }); } function loadLargeData(key) { const count = uni.getStorageSync(`${key}_count`); if (!count) return null; let data = ''; for (let i = 0; i < count; i++) { data += uni.getStorageSync(`${key}_${i}`); } return data; } -
缓存索引:建立快速查找的索引系统
javascript复制const cacheIndex = { 'user_123': { type: 'user', timestamp: 1620000000 }, 'news_456': { type: 'news', timestamp: 1620000001 } }; uni.setStorageSync('cache_index', cacheIndex); // 按类型清理缓存 function cleanCacheByType(type) { const index = uni.getStorageSync('cache_index') || {}; Object.keys(index).forEach(key => { if (index[key].type === type) { uni.removeStorageSync(key); delete index[key]; } }); uni.setStorageSync('cache_index', index); }
5.3 调试技巧
-
网络请求调试:
javascript复制// 在请求拦截器中添加调试日志 http.addRequestInterceptor(options => { console.log('[Request]', options.method, options.url, options.data); return options; }); http.addResponseInterceptor(response => { console.log('[Response]', response.statusCode, response.data); return response; }); -
缓存状态检查:
javascript复制// 打印所有缓存信息 const info = uni.getStorageInfoSync(); console.log('缓存使用情况:', { keys: info.keys, currentSize: info.currentSize, limitSize: info.limitSize }); // 查看特定缓存内容 console.log('userInfo缓存:', uni.getStorageSync('userInfo')); -
性能分析:
javascript复制// 测量请求耗时 const start = Date.now(); await http.request({ url: '/api/data' }); console.log(`请求耗时: ${Date.now() - start}ms`); // 测量缓存读写速度 const data = { /* 大量数据 */ }; let time = Date.now(); uni.setStorageSync('perf_test', data); console.log(`写入耗时: ${Date.now() - time}ms`); time = Date.now(); uni.getStorageSync('perf_test'); console.log(`读取耗时: ${Date.now() - time}ms`);
6. 安全最佳实践
6.1 网络请求安全
-
HTTPS强制使用:
javascript复制// 开发环境检查 if (process.env.NODE_ENV === 'production' && !url.startsWith('https://')) { throw new Error('生产环境必须使用HTTPS'); } -
CSRF防护:
javascript复制// 自动添加CSRF Token http.addRequestInterceptor(options => { const token = getCSRFToken(); // 从cookie或storage获取 options.header = { ...options.header, 'X-CSRF-Token': token }; return options; }); -
请求签名:
javascript复制function signRequest(params, secret) { const sorted = Object.keys(params).sort().map(k => `${k}=${params[k]}`).join('&'); return md5(sorted + secret); } http.addRequestInterceptor(options => { const timestamp = Date.now(); options.data = { ...options.data, timestamp, sign: signRequest({ ...options.data, timestamp }, 'your_secret_key') }; return options; });
6.2 缓存数据安全
-
敏感数据加密:
javascript复制function encryptData(data, key) { // 使用更安全的加密算法,如AES const str = JSON.stringify(data); return CryptoJS.AES.encrypt(str, key).toString(); } function decryptData(encrypted, key) { const bytes = CryptoJS.AES.decrypt(encrypted, key); return JSON.parse(bytes.toString(CryptoJS.enc.Utf8)); } // 使用示例 const encrypted = encryptData({ token: 'abc123' }, 'secret_key'); uni.setStorageSync('auth_data', encrypted); const decrypted = decryptData(uni.getStorageSync('auth_data'), 'secret_key'); -
自动清理机制:
javascript复制// 登录时清理旧缓存 function onLogin() { // 清理一周前的缓存 const oneWeekAgo = Date.now() - 7 * 24 * 3600 * 1000; const info = uni.getStorageInfoSync(); info.keys.forEach(key => { const item = uni.getStorageSync(key); if (item.timestamp && item.timestamp < oneWeekAgo) { uni.removeStorageSync(key); } }); } -
缓存权限控制:
javascript复制const CACHE_WHITELIST = ['settings', 'theme_pref']; function clearNonEssentialCache() { const info = uni.getStorageInfoSync(); info.keys.forEach(key => { if (!CACHE_WHITELIST.includes(key)) { uni.removeStorageSync(key); } }); }
7. 跨平台兼容性处理
7.1 平台差异处理
uni-app虽然支持跨平台,但各平台在实现细节上仍有差异:
-
网络请求差异:
javascript复制// 处理各平台response结构差异 http.addResponseInterceptor(response => { // 微信小程序 if (typeof response.statusCode !== 'undefined') { response.status = response.statusCode; } // H5 else if (typeof response.status !== 'undefined') { response.statusCode = response.status; } return response; }); -
缓存限制差异:
javascript复制function getCacheLimit() { // 各平台缓存限制不同 switch(uni.getSystemInfoSync().platform) { case 'android': return 10 * 1024 * 1024; // 10MB case 'ios': return 5 * 1024 * 1024; // 5MB default: return 5 * 1024 * 1024; // 默认5MB } }
7.2 条件编译处理
使用uni-app的条件编译处理平台差异:
javascript复制// #ifdef MP-WEIXIN
// 微信小程序特有逻辑
const requestTask = wx.request({ ... });
// #endif
// #ifdef H5
// H5特有逻辑
const requestTask = new XMLHttpRequest();
// #endif
// 存储封装示例
function setStorage(key, data) {
// #ifdef MP-WEIXIN
wx.setStorageSync(key, data);
// #endif
// #ifdef H5 || APP-PLUS
localStorage.setItem(key, JSON.stringify(data));
// #endif
}
7.3 降级方案设计
当某些API在某些平台不可用时,提供降级方案:
javascript复制function safeSetStorage(key, data) {
try {
uni.setStorageSync(key, data);
} catch (e) {
// 存储失败降级方案
if (e.message.includes('exceed')) {
// 缓存超出限制,清理旧缓存
cleanOldCache(20);
uni.setStorageSync(key, data);
} else {
// 其他错误使用内存缓存
window.__memoryCache = window.__memoryCache || {};
window.__memoryCache[key] = data;
}
}
}
function safeGetStorage(key) {
try {
return uni.getStorageSync(key);
} catch (e) {
// 从内存缓存读取
return window.__memoryCache?.[key] || null;
}
}
8. 实战案例:电商应用实现
8.1 商品列表实现
结合网络请求和缓存的商品列表页:
javascript复制// pages/goods/list.vue
export default {
data() {
return {
goodsList: [],
page: 1,
loading: false,
hasMore: true
};
},
onLoad() {
this.loadGoods(true);
},
onReachBottom() {
if (!this.loading && this.hasMore) {
this.page++;
this.loadGoods();
}
},
methods: {
async loadGoods(init = false) {
if (this.loading) return;
this.loading = true;
try {
const cacheKey = `goods_${this.page}`;
// 初始化时尝试读取缓存
if (init) {
const cached = uni.getStorageSync(cacheKey);
if (cached) {
this.goodsList = cached.data;
// 即使有缓存也发起网络请求更新数据
this.fetchGoods(cacheKey);
return;
}
}
// 无缓存或非初始化,直接请求
await this.fetchGoods(cacheKey);
} catch (err) {
uni.showToast({
title: '加载失败',
icon: 'none'
});
} finally {
this.loading = false;
}
},
async fetchGoods(cacheKey) {
const res = await http.request({
url: '/goods/list',
method: 'GET',
data: { page: this.page }
});
if (this.page === 1) {
this.goodsList = res.list;
} else {
this.goodsList = [...this.goodsList, ...res.list];
}
this.hasMore = res.hasMore;
// 缓存数据
uni.setStorageSync(cacheKey, {
data: res.list,
timestamp: Date.now()
});
},
refresh() {
this.page = 1;
this.hasMore = true;
uni.removeStorageSync('goods_1');
this.loadGoods(true);
}
}
};
8.2 购物车实现
利用本地缓存实现购物车功能:
javascript复制// store/cart.js
const cart = {
state: {
items: uni.getStorageSync('cart_items') || []
},
addItem(product, quantity = 1) {
const existing = this.state.items.find(item => item.id === product.id);
if (existing) {
existing.quantity += quantity;
} else {
this.state.items.push({
...product,
quantity,
selected: true
});
}
this.save();
},
removeItem(productId) {
this.state.items = this.state.items.filter(item => item.id !== productId);
this.save();
},
updateQuantity(productId, quantity) {
const item = this.state.items.find(item => item.id === productId);
if (item) {
item.quantity = quantity;
this.save();
}
},
toggleSelect(productId) {
const item = this.state.items.find(item => item.id === productId);
if (item) {
item.selected = !item.selected;
this.save();
}
},
selectAll(selected) {
this.state.items.forEach(item => {
item.selected = selected;
});
this.save();
},
clear() {
this.state.items = [];
this.save();
},
save() {
uni.setStorageSync('cart_items', this.state.items);
},
getSelectedItems() {
return this.state.items.filter(item => item.selected);
},
getTotalPrice() {
return this.getSelectedItems().reduce((total, item) => {
return total + (item.price * item.quantity);
}, 0);
}
};
export default cart;
8.3 用户认证流程
结合网络请求和缓存的用户认证实现:
javascript复制// store/auth.js
const auth = {
state: {
user: uni.getStorageSync('user_info') || null,
token: uni.getStorageSync('auth_token') || null
},
async login(credentials) {
try {
const res = await http.request({
url: '/auth/login',
method: 'POST',
data: credentials
});
this.state.user = res.user;
this.state.token = res.token;
// 保存到缓存
uni.setStorageSync('user_info', res.user);
uni.setStorageSync('auth_token', res.token);
// 设置全局请求头
http.addRequestInterceptor(options => {
options.header = {
...options.header,
'Authorization': `Bearer ${this.state.token}`
};
return options;
});
return true;
} catch (err) {
console.error('登录失败:', err);
return false;
}
},
logout() {
this.state.user = null;
this.state.token = null;
// 清除缓存
uni.removeStorageSync('user_info');
uni.removeStorageSync('auth_token');
// 跳转到登录页
uni.reLaunch({
url: '/pages/login/login'
});
},
isAuthenticated() {
return !!this.state.token;
},
async checkAuth() {
if (!this.isAuthenticated()) return false;
try {
// 验证token有效性
await http.request({
url: '/auth/check',
method: 'GET'
});
return true;
} catch (err) {
if (err.statusCode === 401) {
this.logout();
}
return false;
}
}
};
export default auth;
9. 性能监控与异常处理
9.1 网络性能监控
实现网络请求性能数据收集:
javascript复制// 在请求拦截器中记录开始时间
http.addRequestInterceptor(options => {
options.metadata = {
startTime: Date.now()
};
return options;
});
// 在响应拦截器中计算耗时
http.addResponseInterceptor(response => {
const duration = Date.now() - response.config.metadata.startTime;
// 记录性能数据
logPerformance({
url: response.config.url,
method: response.config.method,
status: response.statusCode,
duration,
size: JSON.stringify(response.data).length
});
return response;
});
function logPerformance(metrics) {
const perfData = uni.getStorageSync('network_perf') || [];
perfData.push({
...metrics,
timestamp: Date.now()
});
// 只保留最近100条记录
if (perfData.length > 100) {
perfData.shift();
}
uni.setStorageSync('network_perf', perfData);
}
// 获取性能报告
function getPerformanceReport() {
const data = uni.getStorageSync('network_perf') || [];
const report = {
totalRequests: data.length,
avgDuration: data.reduce((sum, item) => sum + item.duration, 0) / data.length,
byStatus: {},
byEndpoint: {}
};
data.forEach(item => {
// 按状态码统计
report.byStatus[item.status] = (report.byStatus[item.status] || 0) + 1;
// 按端点统计
const endpoint = item.url.split('?')[0];
if (!report.byEndpoint[endpoint]) {
report.byEndpoint[endpoint] = {
count: 0,
totalDuration: 0,
avgDuration: 0
};
}
report.byEndpoint[endpoint].count++;
report.byEndpoint[endpoint].totalDuration += item.duration;
report.byEndpoint[endpoint].avgDuration =
report.byEndpoint[endpoint].totalDuration / report.byEndpoint[endpoint].count;
});
return report;
}
9.2 缓存命中率监控
监控缓存使用效果:
javascript复制const cacheStats = {
hits: 0,
misses: 0,
recordHit() {
this.hits++;
this.save();
},
recordMiss() {
this.misses++;
this.save();
},
get hitRate() {
const total = this.hits + this.misses;
return total > 0 ? (this.hits / total) : 0;
},
save() {
uni.setStorageSync('cache_stats', {
hits: this.hits,
misses: this.misses,
lastUpdated: Date.now()
});
},
load() {
const saved = uni.getStorageSync('cache_stats') || {};
this.hits = saved.hits || 0;
this.misses = saved.misses || 0;
}
};
// 初始化
cacheStats.load();
// 使用示例 - 在缓存查询处
function getWithCache(key) {
const cached = uni.getStorageSync(key);
if (cached) {
cacheStats.recordHit();
return cached;
}
cacheStats.recordMiss();
return null;
}
// 获取统计报告
function getCacheReport() {
return {
hits: cacheStats.hits,
misses: cacheStats.misses,
hitRate: cacheStats.hitRate,
lastUpdated: uni.getStorageSync('cache_stats')?.lastUpdated
};
}
9.3 全局异常处理
实现统一的异常处理机制:
javascript复制// 全局错误处理
function setupErrorHandling() {
// uni-app错误捕获
uni.onError(error => {
logError('UniApp Error', error);
});
// 未处理的Promise rejection
process.on('unhandledRejection', error => {
logError('Unhandled Rejection', error);
});
// 页面JS错误
if (typeof window !== 'undefined') {
window.addEventListener('error', event => {
logError('Window Error', event.error || event.message);
});
}
}
// 错误日志记录
function logError(type, error) {
const errorLog = uni.getStorageSync('error_log') || [];
errorLog.push({
type,
message: error.message || String(error),
stack: error.stack,
timestamp: Date.now(),
page: getCurrentPage()?.route
});
// 限制日志数量
if (errorLog.length > 50) {
errorLog.shift();
}
uni.setStorageSync('error_log', errorLog);
// 生产环境上报错误
if (process.env.NODE_ENV === 'production') {
reportErrorToServer({
type,
message: error.message || String(error),
stack: error.stack
});
}
}
// 获取错误报告
function getErrorReport() {
const errors = uni.getStorageSync('error_log') || [];
const report = {
totalErrors: errors.length,
lastError: errors[errors.length - 1],
errorTypes: {},
frequentErrors: {}
};
// 统计错误类型
errors.forEach(err => {
report.errorTypes[err.type] = (report.errorTypes[err.type] || 0) + 1;
// 统计频繁错误
const key = `${err.type}:${err.message}`;
report.frequentErrors[key] = (report.frequentErrors[key] || 0) + 1;
});
return report;
}
// 初始化错误处理
setupErrorHandling();
10. 高级主题与扩展
10.1 WebSocket与实时数据
结合WebSocket和本地缓存实现实时应用:
javascript复制class RealtimeClient {
constructor(url) {
this.url = url;
this.socket = null;
this.subscribers = {};
this.reconnectAttempts = 0;
this.maxReconnectAttempts = 5;
this.reconnectDelay = 1000;
this.connect();
}
connect() {
this.socket = uni.connectSocket({
url: this.url,
success: () => {
console.log('WebSocket连接成功');
this.reconnectAttempts = 0;
},
fail: (err) => {
console.error('WebSocket连接失败:', err);
this.scheduleReconnect();
}
});
this.socket.onOpen(() => {
console.log('WebSocket已打开');
this.reconnectAttempts = 0;
// 恢复订阅
Object.keys(this.subscribers).forEach(event => {
this.send('subscribe', { event });
});
});
this.socket.onMessage((res) => {
const
