引言
上一章我们探讨了网络请求相关知识点,今天我们来学习跟网络请求同等重要的话题-本地存储。在移动端和H5开发中,数据本地存储至关重要。学会了它能让你的应用更加安全稳固,是提升用户体验至关重要的一环。
为什么需要本地存储?
比方说你每次打开一个APP,都需要重新登录;你放在购物车的商品,页面一刷新就全部消失;你的阅读进度,永远无法被记录……这样的应用体验无疑是灾难性的。
本地存储就是为了解决这些问题而生的。它允许我们将一些非敏感、需要持久化或离线使用的数据直接保存在用户的设备上。对于uni-app开发者来说,理解并用好本地存储,是开发高质量、用户体验良好应用的关键一步。
本地存储的应用场景:
- 用户偏好设置:如主题颜色、字体大小、语言选择。
- 登录状态保存:如存储Token,避免用户每次打开App都要登录。
- 缓存网络数据:将首页列表、文章详情等数据缓存下来,提升二次加载速度,节省用户流量。
- 表单草稿:用户填写了一半的表单,即使退出也可以恢复。
- 浏览记录/购物车:临时存储用户的操作记录。
在uni-app中,我们主要使用 uni.setStorage 系列API来完成本地存储的工作。本文将带你从同步与异步的区别入手,一路深入并结合一个登录状态案例加深对本地存储的理解。
一、 同步与异步存储
很多初学者会困惑,为什么uni-app提供了两套看起来功能一样的存储API?理解它们的区别,是正确使用的第一步。
1.1 存储原理
同步存储:uni.setStorageSync 和 uni.getStorageSync
- “同步”好比“排队”。当代码执行到同步存储语句时,它会阻塞后续所有JS代码的执行,直到本次存储或读取操作彻底完成,才会继续执行下一行代码。就像是超市收银台排队一样,只有一个队伍,你必须等前面的人完全结账离开,你才能开始你的结账过程。
异步存储:uni.setStorage 和 uni.getStorage
- “异步”好比“并行”。当调用异步存储API时,它会立即返回,不会等待操作完成,而是继续执行后面的代码。存储操作在后台默默进行,完成后会通过你提供的回调函数(callback) 来通知你结果。就像你去餐厅吃饭,你点完菜后,不需要在厨房门口干等着。你可以回到座位上玩手机、聊天。等菜做好了,服务员会叫你就行了。
1.2 代码对比
下面我们通过一段代码来感受一下它们的区别。
同步存储:
// 同步存储
try {
console.log('存储用户信息...');
uni.setStorageSync('userInfo', {
name: '马保国', id: 1 });
console.log('用户信息存储成功!');
// 读取数据
const user = uni.getStorageSync('userInfo');
console.log('读取用户信息:', user);
} catch (e) {
// error
console.error('操作失败:', e);
}
// 说明:这里的log会在存储和读取流程结束后才打印
console.log('同步操作结束');
输出顺序永远是:
存储用户信息...
用户信息存储成功!
读取用户信息: {name: '马保国', id: 1}
同步操作结束
异步存储:
// 异步存储
console.log('存储用户信息...');
// 调用setStorage,传入键、值和一个回调函数。它立即返回,不会等待。
uni.setStorage({
key: 'userInfo',
data: {
name: '李保国', id: 2 },
success: () => {
// 这个函数是在存储"成功完成"后才被调用的
console.log('用户信息存储成功!');
// 接着进行异步读取
uni.getStorage({
key: 'userInfo',
success: (res) => {
console.log('读取到的用户信息:', res.data);
},
fail: (err) => {
console.error('读取失败:', err);
}
});
},
fail: (err) => {
// error
console.error('存储失败:', err);
}
});
// 这行代码不会等待存储完成,会立刻执行
console.log('异步操作结束');
输出顺序很可能是:
存储用户信息...
异步操作结束
用户信息存储成功!
读取到的用户信息: {name: '李保国', id: 2}
1.3 如何选择?
| 维度 | 同步存储 (Sync) |
异步存储 |
|---|---|---|
| 代码阻塞 | 会阻塞后续JS执行 | 不会阻塞,立即返回 |
| 代码风格 | 顺序的、同步的,更符合传统开发思维 | 回调地狱,可用Promise/Async/Await优化 |
| 性能影响 | 存储数据量大时可能引起界面卡顿 | 对界面渲染和交互更友好,体验更流畅 |
| 错误处理 | 使用 try...catch |
在 fail 回调或Promise的 .catch() 中处理 |
| 推荐场景 | 数据量小、在页面生命周期函数(如onLoad)中快速存取 |
数据量不确定、在主线程中进行复杂操作时,避免卡顿 |
使用建议:
- V3编译模式下的App平台和H5平台,异步API是真正的异步,不会阻塞JS执行。在小程序端,两者底层实现差异不大。
- 出于性能考虑,除非是简单的初始化数据,否则更推荐使用异步API,尤其是在存储较大数据时。
二、 数据加密与安全
本地存储并非绝对安全。存储在用户设备上的数据,理论上都有被探查和篡改的风险,尤其是在安卓越狱或PC浏览器环境下。因此,对于敏感信息,加密是必不可少的步骤。
2.1 哪些数据需要加密?
- 用户凭证:Token、Session ID
- 个人身份信息:手机号、邮箱、身份证号(原则上不建议存)
- 敏感的应用配置
注意:千万不要把密码明文存储在本地!
2.2 使用Crypto-JS进行AES加密
AES是一种对称加密算法,速度快且安全。我们使用一个流行的库 crypto-js来进行加密。
1. 安装库:
npm install crypto-js
2. 封装加密存储工具类:
我们创建一个 secureStorage.js 文件。
// utils/secureStorage.js
import CryptoJS from 'crypto-js';
// !!!重要!!! 密钥不能明文写在前端代码里。
// 这里为了演示方便直接写死。生产环境应通过服务端下发方式获取,或结合设备指纹生成。
const SECRET_KEY = 'your_32_byte_secret_key_123456!'; // 密钥必须是16, 24或32字节
export const secureStorage = {
/**
* 加密并存储数据
* @param {string} key - 存储的键
* @param {any} data - 要存储的原始数据
*/
setItem(key, data) {
return new Promise((resolve, reject) => {
try {
// 1. 将数据转换为字符串
const dataString = JSON.stringify(data);
// 2. 使用AES加密
const encryptedData = CryptoJS.AES.encrypt(dataString, SECRET_KEY).toString();
// 3. 将加密后的密文存储到本地
uni.setStorage({
key: key,
data: encryptedData,
success: resolve,
fail: reject
});
} catch (error) {
reject(error);
}
});
},
/**
* 读取并解密数据
* @param {string} key - 存储的键
* @returns {Promise<any>} 解密后的原始数据
*/
getItem(key) {
return new Promise((resolve, reject) => {
uni.getStorage({
key: key,
success: (res) => {
try {
// 1. 获取加密的密文
const encryptedData = res.data;
// 2. 使用AES解密
const bytes = CryptoJS.AES.decrypt(encryptedData, SECRET_KEY);
// 3. 将解密后的字节数组转换为UTF-8字符串
const decryptedString = bytes.toString(CryptoJS.enc.Utf8);
if (decryptedString) {
// 4. 将字符串解析为原始对象
const originalData = JSON.
uni-app本地存储实战指南


最低0.47元/天 解锁文章
791

被折叠的 条评论
为什么被折叠?



