夜雨聆风学习资料网

ARTICLE · 1138301

uni-app 本地缓存策略:让应用「离线也能用」

uni-app 本地缓存策略:让应用「离线也能用」
数据层模块的收尾篇。前面把请求、登录态、文件都收进了统一方案,这篇补最后一块:本地缓存。直接裸用 uni.setStorageSync 能跑,但裸用三个月后一定会遇到「旧数据结构卡死页面」「缓存满了写不进去」这类怪问题。这篇给出一套带过期、带版本、带兜底的缓存封装,外加「缓存优先渲染」的秒开策略。

一、先知道家底:三端的限额和差异

•小程序:单 key 上限 1MB,总上限 10MB,超限写入静默失败(这是最阴的,不报错,数据就是没存上)

•H5:走 localStorage,约 5MB,存的是字符串

•App:native storage,额度宽松,但同步 API 照样阻塞 UI 线程

两个结论:小程序端的缓存总量要有规划,别什么都往里塞;所有同步 storage 调用尽量别在循环里高频执行,大对象序列化一次几十毫秒,列表页滚动时会掉帧。

二、基础封装:序列化、命名空间、异常兜底

// utils/storage.jsconst PREFIX = 'myapp:'   // 命名空间:清理时按前缀扫,调试时一眼认出是谁写的export function setCache(key, value) {  try {    uni.setStorageSync(PREFIX + key, JSON.stringify(value))  } catch (e) {    // 小程序超限静默失败,这里能抓到 H5 的 QuotaExceededError    console.warn('[storage 写入失败]', key, e)    clearExpired()   // 清一轮过期数据再试    try { uni.setStorageSync(PREFIX + key, JSON.stringify(value)) } catch (e2) {}  }}export function getCache(key) {  try {    const raw = uni.getStorageSync(PREFIX + key)    return raw ? JSON.parse(raw) : null  } catch (e) {    // JSON 解析失败说明数据脏了,删掉当没存过    uni.removeStorageSync(PREFIX + key)    return null  }}
try-catch 包住 getCache 不是多虑:版本升级改了数据结构、或者旧版本存了非 JSON 字符串,parse 一下就抛异常,页面白屏。缓存坏了应当作没存过,绝不能让它弄崩页面。

三、过期时间与版本号

缓存最大的坑是「永远新鲜」,包一层元信息就能解决:

过期管新鲜度,版本管结构变更,两件事别混在一个字段里
const CACHE_VERSION = 2export function setCacheV(key, value, ttl) {  setCache(key, {    data: value,    expireAt: ttl ? Date.now() + ttl : Infinity,    version: CACHE_VERSION  })}export function getCacheV(key) {  const wrap = getCache(key)  if (!wrap) return null  if (wrap.version !== CACHE_VERSION) return null        // 结构变了,作废  if (Date.now() > wrap.expireAt) {                       // 过期了,顺手删掉    uni.removeStorageSync(PREFIX + key)    return null  }  return wrap.data}

四、缓存优先渲染:秒开的关键

列表页、详情页的通用优化:先渲染缓存数据,同时发请求,回来后覆盖刷新。用户看到的是「瞬间有内容」而不是「白屏转圈」:

async function loadGoodsList() {  const cached = getCacheV('goods:list')  if (cached) {    list.value = cached          // 先上缓存,立刻可看  }  const fresh = await request('/goods/list', { silent: !cached })  list.value = fresh  setCacheV('goods:list', fresh, 5 * 60 * 1000)   // 5 分钟 TTL}

这个策略的细节在「有无缓存」的分叉里:有缓存时请求走 silent 不开 loading,避免闪一下;没缓存时正常 loading。请求失败且有缓存时直接吞掉错误,弱网环境下页面依然可用,这就是「离线也能用」的底气。

注意边界:涉及钱和交易状态的数据别用缓存优先渲染。余额、订单状态展示旧值,用户可能做出错误决策,这类数据宁可白屏也要拉最新的。

五、清理策略:给缓存留个出口

•按前缀清理:登出时用 uni.getStorageInfoSync() 拿全部 key,过滤自家前缀批量删,别把别的业务数据误伤

•启动时清理:App onLaunch 里扫一遍过期项,控制总量

•上限淘汰:给关键业务缓存定上限条数,超了按写入时间删最旧的,模拟 LRU

· · ·

这篇收掉了数据层模块的最后一块:storage 三端限额心里有数(小程序超限静默失败尤其要记牢),封装层用 try-catch 兜住脏数据,元信息里 expireAt 管新鲜度、version 管结构变更,列表页用缓存优先渲染做到秒开和弱网可用,清理按前缀、按过期、按上限三条路走。至此请求、登录态、文件、缓存四件基建全部就位,业务页面往上是纯 happy path。

觉得有用的话,点赞、分享、推荐三连支持一下。

相关学习资料