《uni-app跨平台开发完全指南》- 10 - 本地存储

uni-app本地存储实战指南

引言

上一章我们探讨了网络请求相关知识点,今天我们来学习跟网络请求同等重要的话题-本地存储。在移动端和H5开发中,数据本地存储至关重要。学会了它能让你的应用更加安全稳固,是提升用户体验至关重要的一环。
在这里插入图片描述

为什么需要本地存储?

比方说你每次打开一个APP,都需要重新登录;你放在购物车的商品,页面一刷新就全部消失;你的阅读进度,永远无法被记录……这样的应用体验无疑是灾难性的。

本地存储就是为了解决这些问题而生的。它允许我们将一些非敏感、需要持久化或离线使用的数据直接保存在用户的设备上。对于uni-app开发者来说,理解并用好本地存储,是开发高质量、用户体验良好应用的关键一步。

本地存储的应用场景:

  • 用户偏好设置:如主题颜色、字体大小、语言选择。
  • 登录状态保存:如存储Token,避免用户每次打开App都要登录。
  • 缓存网络数据:将首页列表、文章详情等数据缓存下来,提升二次加载速度,节省用户流量。
  • 表单草稿:用户填写了一半的表单,即使退出也可以恢复。
  • 浏览记录/购物车:临时存储用户的操作记录。

在uni-app中,我们主要使用 uni.setStorage 系列API来完成本地存储的工作。本文将带你从同步与异步的区别入手,一路深入并结合一个登录状态案例加深对本地存储的理解。


一、 同步与异步存储

很多初学者会困惑,为什么uni-app提供了两套看起来功能一样的存储API?理解它们的区别,是正确使用的第一步。

1.1 存储原理

同步存储uni.setStorageSyncuni.getStorageSync

  • “同步”好比“排队”。当代码执行到同步存储语句时,它会阻塞后续所有JS代码的执行,直到本次存储或读取操作彻底完成,才会继续执行下一行代码。就像是超市收银台排队一样,只有一个队伍,你必须等前面的人完全结账离开,你才能开始你的结账过程。

异步存储uni.setStorageuni.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.
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

QuantumLeap丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值