Uniapp本地存储实战:从setStorage到getStorageSync的异步与同步抉择(附场景Demo)
1. 为什么需要本地存储?
在移动应用开发中,本地存储就像是一个随身携带的小本子。想象一下,每次打开应用都要重新登录有多麻烦?有了本地存储,我们可以把用户登录状态、个性化设置等关键信息保存在设备上,下次打开应用时直接读取,大大提升用户体验。
Uniapp提供了两套本地存储API:异步的setStorage/getStorage和同步的setStorageSync/getStorageSync。我在开发一个跨平台电商应用时就遇到过选择困难:商品详情页需要快速读取用户收藏状态(适合同步),而用户登录信息需要安全校验(适合异步)。这两套API看似功能相同,但底层实现和适用场景却大不相同。
2. 异步存储:setStorage与getStorage详解
2.1 异步存储的工作原理
异步存储就像点外卖:下单后你可以继续做其他事情,等外卖到了会通知你。setStorage的典型用法是这样的:
uni.setStorage({
key: 'userToken',
data: 'abcd1234',
success: () => {
console.log('令牌存储成功');
},
fail: (err) => {
console.error('存储失败', err);
}
});
我在实际项目中发现三个关键点:
- 回调函数是异步执行的,不会阻塞页面渲染
- 适合存储较大数据(比如用户浏览历史)
- 错误处理更完善,可以通过fail回调捕获异常
2.2 异步读取的实战技巧
getStorage有个容易踩坑的地方:回调函数的执行时机。有次我做表单草稿保存功能,这样写就出问题了:
let draftData;
uni.getStorage({
key: 'formDraft',
success: (res) => {
draftData = res.data; // 这里赋值时,后面的代码已经执行了
}
});
console.log(draftData); // 这里会输出undefined
正确的做法是把后续逻辑都放在success回调里,或者用Promise封装:
function getStoragePromise(key) {
return new Promise((resolve, reject) => {
uni.getStorage({
key,
success: resolve,
fail: reject
});
});
}
// 使用时
const draftData = await getStoragePromise('formDraft');
3. 同步存储:setStorageSync与getStorageSync深度解析
3.1 同步方法的特点
同步方法就像在食堂打饭:必须排队等到饭菜到手才能离开。它的代码更简洁:
// 存储
uni.setStorageSync('theme', 'dark');
// 读取
const theme = uni.getStorageSync('theme');
但我在性能测试时发现:同步操作会阻塞JS主线程。当存储5MB以上的数据时,界面会出现明显卡顿。因此同步方法最适合:
- 高频读写的小数据(如用户偏好设置)
- 应用启动时必须立即获取的数据
- 简单的状态标记存储
3.2 同步读取的异常处理
同步方法没有回调函数,错误处理需要配合try-catch:
try {
const token = uni.getStorageSync('userToken');
if(!token) throw new Error('令牌不存在');
// 后续逻辑...
} catch (e) {
console.error('读取令牌失败', e);
uni.showToast({ title: '请重新登录' });
}
有个实际案例:某次APP更新后,旧版存储的数据格式不兼容新版,导致同步读取直接崩溃。后来我们增加了数据校验逻辑:
function safeGetSync(key) {
try {
const data = uni.getStorageSync(key);
// 验证数据格式
if(key === 'userInfo' && !data?.userId) {
return null;
}
return data;
} catch {
return null;
}
}
4. 异步与同步的抉择指南
4.1 性能对比实测
我用同一台测试机做了组对比实验(存储100KB数据,循环100次):
| 操作类型 | 平均耗时 | 主线程阻塞 |
|---|---|---|
| setStorage | 12ms | 无 |
| setStorageSync | 8ms | 轻微 |
| getStorage | 10ms | 无 |
| getStorageSync | 6ms | 轻微 |
看起来同步更快?但实际开发中要考虑更多因素:
- 异步操作的并行优势
- 错误恢复能力
- 代码可维护性
4.2 典型场景推荐方案
根据我的项目经验,这些场景推荐这样选:
必须用异步的场景:
- 启动时加载用户配置(需要网络请求配合)
- 表单数据自动保存(需要错误重试机制)
- 需要加密存储的敏感数据
适合同步的场景:
- 界面主题切换
- 全局loading状态
- 简单的AB测试标记
- 页面间临时参数传递
4.3 混合使用的最佳实践
很多时候需要混搭使用。比如用户登录流程:
// 同步快速检查登录状态
const token = uni.getStorageSync('token');
if(token) {
// 异步验证令牌有效性
checkToken(token).then(valid => {
if(!valid) {
uni.removeStorageSync('token'); // 同步清除
showLoginModal();
}
});
} else {
showLoginModal();
}
5. 实战Demo:购物车本地缓存方案
让我们实现一个完整的购物车本地存储方案:
// 购物车服务模块
const cartService = {
// 异步保存(防抖处理)
saveCart: debounce(async (cartItems) => {
try {
await new Promise((resolve, reject) => {
uni.setStorage({
key: 'cart',
data: JSON.stringify(cartItems),
success: resolve,
fail: reject
});
});
console.log('购物车保存成功');
} catch (e) {
console.error('保存失败', e);
// 尝试降级方案
try {
uni.setStorageSync('cart_fallback', JSON.stringify(cartItems));
} catch (syncErr) {
uni.reportAnalytics('STORAGE_ERROR', {...});
}
}
}, 500),
// 同步读取(首屏快速展示)
getCartSync: () => {
try {
const data = uni.getStorageSync('cart') ||
uni.getStorageSync('cart_fallback');
return data ? JSON.parse(data) : [];
} catch {
return [];
}
},
// 异步读取(确保最新)
getCartAsync: () => {
return new Promise((resolve) => {
uni.getStorage({
key: 'cart',
success: (res) => {
try {
resolve(JSON.parse(res.data));
} catch {
resolve([]);
}
},
fail: () => resolve(this.getCartSync())
});
});
}
};
这个方案实现了:
- 首屏同步快速展示
- 修改时异步防抖保存
- 完整的错误降级处理
- 异常监控上报
6. 常见问题与性能优化
6.1 存储大小限制
不同平台限制不同:
- 微信小程序:10MB
- H5:取决于浏览器,通常5-10MB
- App:理论上无限制,但建议不超过50MB
我遇到过H5端存储图片base64导致超出限额的案例,解决方案是:
- 定期清理过期数据
- 大数据改用文件系统存储
- 压缩存储内容
6.2 数据加密方案
敏感数据应该加密存储。推荐使用crypto-js:
import CryptoJS from 'crypto-js';
const SECRET_KEY = 'your-secret-key';
function encryptData(data) {
return CryptoJS.AES.encrypt(
JSON.stringify(data),
SECRET_KEY
).toString();
}
function decryptData(ciphertext) {
const bytes = CryptoJS.AES.decrypt(ciphertext, SECRET_KEY);
return JSON.parse(bytes.toString(CryptoJS.enc.Utf8));
}
// 使用示例
uni.setStorageSync('user', encryptData(userInfo));
const user = decryptData(uni.getStorageSync('user'));
6.3 多Tab数据同步
在H5环境中,多个浏览器Tab间的存储需要同步。可以通过监听storage事件实现:
window.addEventListener('storage', (event) => {
if(event.key === 'cart') {
// 更新本地购物车状态
this.cartData = JSON.parse(event.newValue);
}
});
在Uniapp中,还可以使用全局事件总线:
// 存储时触发事件
uni.$emit('storageUpdate', { key: 'cart', value: newCart });
// 其他页面监听
uni.$on('storageUpdate', ({key, value}) => {
if(key === 'cart') {
this.cartData = value;
}
});
7. 跨平台兼容性处理
不同平台的存储实现有差异,需要特别注意:
-
微信小程序:
- 同步API性能最好
- 清除缓存会同时清除storage
-
H5:
- 受浏览器同源策略限制
- 隐私模式可能无法使用storage
-
App:
- 支持最大的存储空间
- 可以配合SQLite做结构化存储
建议在应用启动时做能力检测:
function checkStorage() {
try {
const testKey = '__test__';
uni.setStorageSync(testKey, '1');
if(uni.getStorageSync(testKey) !== '1') {
throw new Error('存储异常');
}
uni.removeStorageSync(testKey);
return true;
} catch {
uni.showModal({
title: '提示',
content: '本地存储功能异常,部分功能可能受限',
showCancel: false
});
return false;
}
}
8. 高级技巧:存储监控与自动化
8.1 存储状态监控
可以通过拦截器监控所有存储操作:
const originalSet = uni.setStorage;
uni.setStorage = function(options) {
console.log(`[Storage] 设置 ${options.key}`);
performance.mark('storageStart');
return originalSet.call(this, {
...options,
complete: () => {
performance.mark('storageEnd');
performance.measure(
'storageTime',
'storageStart',
'storageEnd'
);
options.complete?.();
}
});
};
8.2 自动过期机制
实现类似cookie的过期时间功能:
function setWithExpiry(key, value, expiryDays) {
const item = {
value,
expiry: Date.now() + expiryDays * 86400000
};
uni.setStorageSync(key, JSON.stringify(item));
}
function getWithExpiry(key) {
const itemStr = uni.getStorageSync(key);
if(!itemStr) return null;
const item = JSON.parse(itemStr);
if(Date.now() > item.expiry) {
uni.removeStorageSync(key);
return null;
}
return item.value;
}
8.3 存储压缩技巧
对于大量重复数据,可以使用压缩算法:
import { compress, decompress } from 'lz-string';
function setCompressed(key, data) {
uni.setStorageSync(key, compress(JSON.stringify(data)));
}
function getCompressed(key) {
const data = uni.getStorageSync(key);
return data ? JSON.parse(decompress(data)) : null;
}
更多推荐


所有评论(0)