前端缓存优化
缓存优化的核心是在**"速度"与"数据新鲜度"之间找到最佳平衡点。结合你之前的 uni-app/小程序背景,缓存优化不仅是"存起来",更要考虑 存储空间限制(微信小程序仅10MB)和异步读取的耗时**。
我把缓存优化分为四个层级来拆解,按投入产出比排序如下:
🗂️ 第一层:静态资源缓存(网络层)
这是性价比最高的优化,让图片、JS、CSS 等文件**"强缓存"**在用户本地,避免重复下载。
- 核心机制 :服务端响应头设置
Cache-Control(强制缓存)和ETag(协商缓存)。 - 最佳实践 :
- 带 Hash 的永久缓存 :构建工具(Vite/Webpack)给文件名加上
hash值(如main.a3f2e1.js)。文件内容不变,Hash 不变;内容一变,URL 就变。此时可设置Cache-Control: max-age=31536000(一年),用户永远只下载一次,除非版本更新。 - CDN 加速 :将静态资源上传至 CDN,利用 CDN 的边缘节点就近返回数据。注意在
manifest.json中配置 CDN 地址作为资源基础路径。
- 带 Hash 的永久缓存 :构建工具(Vite/Webpack)给文件名加上
📦 第二层:接口数据缓存(业务层)
这是体验改善最明显的一层,用于缓存列表、详情、配置等非实时数据。
- 内存缓存(最快) :在 Vuex/Pinia/Zustand 的 Store 中存一份。页面返回时,0ms 读取,瞬间展示。注意:页面刷新或小程序冷启动会丢失。
- 本地磁盘缓存(持久化) :利用
uni.setStorage/wx.setStorage(小程序异步 API)。-
策略 1:先缓存后网络(Stale-While-Revalidate):打开页面时,先立即读取本地缓存展示旧数据,同时发起网络请求。请求成功后,用新数据更新页面和缓存。用户感知不到白屏。
-
策略 2:手动封装请求缓存(防重复请求) :在
uni.request外层封装一层。如果 500ms 内发起两次相同的 API 请求(如首页并发请求),只发一次网络请求,第二个直接复用第一个的 Promise。 -
代码示例(请求缓存池) :
javascript// 简易请求去重器 const pendingMap = new Map(); function requestWithCache(url, params) { const key = url + JSON.stringify(params); // 如果正在请求中,直接返回之前的 Promise if (pendingMap.has(key)) { return pendingMap.get(key); } const promise = uni.request({ url, data: params }).then(res => { pendingMap.delete(key); // 请求完毕清除 // 同时存入 Storage(异步) uni.setStorage({ key: `cache_${key}`, data: res.data }); return res; }); pendingMap.set(key, promise); return promise; }
-
🖼️ 第三层:图片与渲染缓存(视觉层)
- 小程序图片缓存 :
<image>组件默认有缓存,但不总是可控。如果使用uni-app,建议在image上加上webp属性(体积更小),并开启lazy-load。 - 骨架屏与占位缓存 :将加载中的骨架屏代码嵌入到 WXML/Vue 模板中。首次加载展示骨架,数据回来后替换。配合
Storage记录"是否首次访问",如果是老用户,甚至可以直接先渲染缓存数据,再异步请求刷新。
⚙️ 第四层:小程序独有的缓存机制(代码包与编译)
- 更新机制 :微信小程序冷启动时会异步检查代码包是否有新版本。但下载新包是静默的,下一次冷启动 才会使用新包。
- 主动更新策略 :如果发现版本更新频繁,可以在
App.onLaunch中调用wx.getUpdateManager,强制用户重启应用更新,避免旧缓存代码与新接口不匹配报错。
- 主动更新策略 :如果发现版本更新频繁,可以在
- Storage 容量警戒 :小程序单 key 限制 1MB,整体上限 10MB。需要做 LRU(最近最少使用)策略 :存数据时检查总大小,如果接近 10MB,优先删除最旧的、非核心的缓存数据,防止
setStorage写入失败。
🧠 缓存的"灵魂三问"(失效策略)
缓存最怕"脏数据"(显示旧数据)。必须配套清晰的失效机制:
- 按时间失效(TTL) :存数据时带上时间戳
{ data: {...}, expire: Date.now() + 60000 }。读取时判断是否过期,过期就发起请求。 - 按事件失效(主动清除) :用户退出登录、支付成功、发布评论后,主动调用
uni.removeStorageSync('home_list')清除相关缓存,而不是等过期。 - 按版本失效 :当 App 发版(
manifest.json中的versionName变更)时,在启动时清空所有旧缓存(uni.clearStorage),避免新旧数据结构不兼容。
💡 针对你当前场景的特别建议
如果你正在做商品详情页或信息流列表:
- 详情页:非常推荐"先缓存后网络"策略。因为商品名、图片、价格短时间内不会大变,秒开体验远超转菊花。
- 列表页(带分页) :不要缓存整个列表滚动位置,容易导致内存溢出。只需缓存第一页的数据。用户返回时,先展示第一页旧数据,再请求最新数据,并提示"已更新 N 条"。
你可以先上手封装那个**"请求去重器"**,这几乎是零成本换来并发接口速度翻倍。如果针对某个特定场景(比如表单回填或搜索建议)的缓存拿不准,可以具体说说,我再给你针对性代码。