你肯定遇到过这种情况:在地铁上、电梯里,手机信号只有一格,打开一个小程序,转圈、白屏、提示“网络不给力”。用户等不了三秒就关掉了。
能不能让小程序在没有网络的时候也能打开,至少能看到之前看过的内容?答案是肯定的。今天聊聊小程序的网络请求优化和离线缓存策略。
传统App安装后会占用几百MB空间,很多资源和数据都存在本地。小程序不同,它每次打开需要从服务器拉取代码包和业务数据。
这导致一个天然劣势:依赖网络。但反过来也给了我们一个机会——既然数据要拉取,我们可以设计一套策略,让小程序在弱网甚至无网时依然可用。
核心原则只有一条:不要每次都去问服务器要数据。
用户第一次打开小程序时,我们把数据存下来。第二次打开时,先展示本地数据,同时在后台偷偷去服务器拉取最新数据。用户感知不到等待。
这个思路在三个层面上可以实施。
小程序提供了同步和异步的Storage API,可以把数据存在用户手机里。
// 保存数据 wx.setStorageSync('userInfo', { name: '张三', avatar: '...' }) // 读取数据 const userInfo = wx.getStorageSync('userInfo') if (userInfo) { // 先展示缓存的数据 this.setData({ userInfo }) } // 然后在后台请求最新数据 wx.request({ url: 'https://api.example.com/user/info', success: (res) => { this.setData({ userInfo: res.data }) wx.setStorageSync('userInfo', res.data) } })
但Storage有容量限制,单个小程序最大10MB。不能什么都往里存。
对于不太变化的数据,可以在后端接口响应头里加上Cache-Control。
后端返回示例:
Cache-Control: max-age=3600
小程序发起请求时,如果开启了forceUpdate,可以这样配置:
wx.request({ url: 'https://api.example.com/config', enableCache: true, // 开启后,小程序会自动缓存请求结果 success: (res) => { ... } })
enableCache是微信小程序基础库提供的特性,会按照HTTP缓存规范自动处理,开发者不需要手动读写Storage。
对于一些重要的页面或数据,可以在小程序启动时提前拉取并缓存。
// app.js onLaunch() { // 提前缓存首页数据 this.prefetchHomeData() // 缓存常用配置 this.prefetchCommonConfig() }, prefetchHomeData() { wx.request({ url: 'https://api.example.com/home', success: (res) => { wx.setStorageSync('homeData', res.data) } }) }
用户打开首页时,如果网络慢,直接读缓存。如果网络快,缓存数据已经被更新了。
用户进入电梯后完全没网,这时候怎么处理?
首先,要在请求失败时给出友好提示,而不是白屏或崩溃。
function fetchData() { wx.request({ url: 'https://api.example.com/list', success: (res) => { this.setData({ list: res.data }) wx.setStorageSync('listData', res.data) }, fail: (err) => { // 网络请求失败,尝试从缓存读取 const cached = wx.getStorageSync('listData') if (cached) { this.setData({ list: cached }) wx.showToast({ title: '当前为离线数据', icon: 'none' }) } else { wx.showToast({ title: '网络不给力,请稍后重试', icon: 'none' }) } } }) }
其次,对于需要提交数据的场景(比如用户填写表单后点击提交),可以先把数据存在本地,等网络恢复后再自动上传。
function submitForm(data) { wx.request({ url: 'https://api.example.com/submit', method: 'POST', data: data, success: (res) => { wx.showToast({ title: '提交成功' }) }, fail: () => { // 保存到待提交队列 const queue = wx.getStorageSync('pendingQueue') || [] queue.push(data) wx.setStorageSync('pendingQueue', queue) wx.showToast({ title: '已保存,网络恢复后自动提交', icon: 'none' }) } }) } // 在app.js中监听网络恢复 wx.onNetworkStatusChange((res) => { if (res.isConnected) { flushPendingQueue() } })
缓存带来的最大问题是数据不一致。用户看到的是旧数据,可能产生误解。
解决方案是在界面上明确标识数据的新鲜度。
data: { list: [], dataSource: 'cache' // cache 或 network } // 在界面上显示 <view class="data-tip" wx:if="{{dataSource === 'cache'}}"> 当前为缓存数据,正在更新... </view>
另一个问题是缓存占用空间越来越大。虽然小程序会自动清理,但作为负责任的开发者,应该主动管理。
// 设置缓存过期时间 function setCacheWithExpiry(key, data, ttl = 3600000) { // 默认1小时 const item = { data: data, timestamp: Date.now(), ttl: ttl } wx.setStorageSync(key, item) } function getCacheWithExpiry(key) { const item = wx.getStorageSync(key) if (!item) return null if (Date.now() - item.timestamp > item.ttl) { wx.removeStorageSync(key) return null } return item.data }
不是所有数据都需要同样的缓存策略。
静态配置数据:Cache-Control设置较长时间,比如一天。小程序启动时优先读缓存,后台静默更新。
用户个人信息:缓存到Storage,更新频率低,每次用户修改后主动更新缓存。
列表数据:缓存最近3页,设置较短的过期时间(比如5分钟)。用户下拉刷新时强制请求最新数据。
图片资源:使用image组件的lazy-load和本地缓存机制。小程序会自动缓存已加载过的图片。
实时性要求高的数据:比如股票价格、比赛比分、消息通知,不应该读缓存。但可以先用缓存的旧数据占位,等新数据回来后替换。
技术手段之外,产品设计也能起作用。
首屏内容尽量使用静态数据或本地数据,不要上来就请求三个接口。骨架屏不只是为了美观,更是为了让用户在等待时不至于看到白屏。
对核心流程做离线化设计。比如一个公交查询小程序,可以把全城的线路数据在WiFi环境下预下载到本地。用户离线时仍然可以查线路、算换乘,只是无法获取实时车辆位置。
优化完成后,用微信开发者工具的“网络”面板模拟弱网或离线环境。选择“Slow 3G”看表现,选择“Offline”看提示是否友好。
更真实的测试:把手机调到飞行模式,打开小程序,看看能不能完成核心操作。
一个健康的离线策略,不应该让用户感觉到“断网了”。用户应该只是发现“数据可能不是最新的”,而不是“用不了了”。