订单数据缓存 以及针对海量库存数据的 懒加载+数据分片 的具体实现方式,结合UniApp+微信小程序的技术栈,分两部分详细拆解落地思路和核心代码,既讲逻辑也给可落地的实现方案。
一、订单数据缓存的实现(低网环境适配核心)
业务背景
汽配商户常处于仓库等低网/断网环境,开单过程中如果网络中断,已填写的订单数据不能丢失;网络恢复后需自动同步,因此缓存核心是「本地持久化+合理的同步策略」。
实现思路&核心代码
我基于微信小程序的本地存储API(wx.setStorageSync/wx.getStorageSync)封装了缓存工具类,结合「临时缓存+持久缓存+过期策略」实现,具体步骤如下:
1. 第一步:设计缓存策略
| 缓存类型 | 适用场景 | 存储位置 | 过期策略 |
|---|---|---|---|
| 临时缓存 | 订单草稿(未提交) | 微信临时缓存 | 小程序退出后自动清理 |
| 持久缓存 | 已提交待同步的订单 | 微信本地持久存储 | 同步成功后删除,7天过期 |
2. 第二步:封装通用缓存工具类(核心复用)
typescript
// utils/cache.ts - 统一管理缓存逻辑
/**
* 订单数据缓存工具类
* @param key 缓存键名(区分不同订单:order_draft_+开单员ID+时间戳)
* @param data 要缓存的订单数据
* @param isPersistent 是否持久缓存(默认临时)
*/
export const orderCache = {
// 设置缓存
set: (key: string, data: any, isPersistent = false) => {
const cacheData = {
data,
timestamp: Date.now(), // 记录缓存时间,用于过期判断
isPersistent // 标记是否持久缓存
};
// 临时缓存用wx.setStorage(异步),持久缓存用同步存储(确保数据落地)
if (isPersistent) {
wx.setStorageSync(`persist_${key}`, JSON.stringify(cacheData));
} else {
wx.setStorage({
key: `temp_${key}`,
data: JSON.stringify(cacheData),
fail: () => {
console.error('订单缓存失败,低网环境请注意手动保存');
}
});
}
},
// 获取缓存
get: (key: string, isPersistent = false) => {
const cacheKey = isPersistent ? `persist_${key}` : `temp_${key}`;
const cacheStr = isPersistent ? wx.getStorageSync(cacheKey) : wx.getStorageSync(cacheKey);
if (!cacheStr) return null;
const cacheData = JSON.parse(cacheStr);
// 过期判断:持久缓存7天过期(604800000ms),临时缓存无过期但小程序退出失效
if (isPersistent && (Date.now() - cacheData.timestamp) > 604800000) {
this.remove(key, isPersistent); // 清理过期缓存
return null;
}
return cacheData.data;
},
// 删除缓存(同步成功后调用)
remove: (key: string, isPersistent = false) => {
const cacheKey = isPersistent ? `persist_${key}` : `temp_${key}`;
wx.removeStorageSync(cacheKey);
},
// 同步缓存订单到后端(网络恢复时触发)
syncCacheOrder: async (key: string) => {
const cacheOrder = this.get(key, true);
if (!cacheOrder) return;
try {
// 调用后端同步接口
const res = await uni.request({
url: '/api/order/sync',
method: 'POST',
data: cacheOrder
});
if (res.data.code === 200) {
this.remove(key, true); // 同步成功,删除缓存
uni.showToast({ title: '订单同步成功' });
}
} catch (err) {
console.error('订单同步失败,保留缓存:', err);
}
}
};
3. 第三步:业务层集成(开单页面)
在开单页面的输入、选择配件等操作时,实时缓存 数据;页面初始化时先读取缓存,避免数据丢失:
vue
<!-- pages/order/create.vue -->
<script setup lang="ts">
import { orderCache } from '@/utils/cache';
import { ref, onMounted, onUnload } from 'vue';
// 订单唯一标识(开单员ID+当前时间戳)
const orderKey = `order_draft_${uni.getStorageSync('userId')}_${Date.now()}`;
// 订单表单数据
const orderForm = ref({
partsList: [], // 配件列表
customerInfo: {}, // 客户信息
totalAmount: 0, // 总金额
status: 'draft' // 草稿状态
});
// 页面初始化:读取缓存的订单草稿
onMounted(() => {
const cacheData = orderCache.get(orderKey);
if (cacheData) {
orderForm.value = cacheData; // 恢复缓存的草稿数据
uni.showToast({ title: '恢复离线草稿数据' });
}
// 监听网络状态,恢复网络时自动同步持久缓存的订单
wx.onNetworkStatusChange((res) => {
if (res.isConnected) {
orderCache.syncCacheOrder(orderKey);
}
});
});
// 输入框/选择器变化时,实时缓存
const handleFormChange = () => {
orderCache.set(orderKey, orderForm.value);
};
// 提交订单:转为持久缓存(待同步)
const submitOrder = () => {
if (!uni.getNetworkType().networkType === 'none') {
// 断网:持久缓存,标记待同步
orderCache.set(orderKey, orderForm.value, true);
uni.showToast({ title: '断网已保存,恢复网络后自动提交' });
} else {
// 有网:直接提交接口
// ... 接口请求逻辑
}
};
</script>
核心要点
- 用
JSON.stringify/parse处理本地存储的序列化(小程序本地存储仅支持字符串); - 区分临时/持久缓存,避免无效数据占用存储;
- 监听网络状态(
wx.onNetworkStatusChange),实现断网缓存、联网自动同步。
二、库存数据懒加载+数据分片的实现(性能优化核心)
业务背景
库存数据量极大(49448品种/29728数量),一次性加载所有数据会导致页面卡顿、内存溢出,因此采用「后端分页(数据分片)+ 前端滚动懒加载」的方案,仅加载当前可视区域数据。
实现思路&核心代码
1. 第一步:后端接口改造(数据分片)
后端提供分页接口,接收 pageNum(页码)、pageSize(每页条数)参数,返回「当前页数据+总条数」,示例返回格式:
json
{
"code": 200,
"data": {
"list": [/* 每页库存数据 */],
"total": 49448, // 总条数
"pageNum": 1,
"pageSize": 20
}
}
2. 第二步:前端懒加载实现(滚动加载下一页)
基于UniApp的 onReachBottom(页面触底)生命周期,结合分页状态管理,实现滚动加载:
vue
<!-- pages/stock/list.vue -->
<script setup lang="ts">
import { ref, onMounted, onReachBottom } from 'vue';
import { getStockList } from '@/api/stock'; // 库存列表接口
// 分页参数
const pageParams = ref({
pageNum: 1,
pageSize: 20,
total: 0, // 总条数
loading: false, // 加载中状态(防止重复请求)
finished: false // 加载完成(无更多数据)
});
// 库存列表数据(仅存储已加载的分片数据)
const stockList = ref([]);
// 加载库存数据(分片请求)
const loadStockData = async () => {
if (pageParams.value.loading || pageParams.value.finished) return;
pageParams.value.loading = true;
try {
const res = await getStockList({
pageNum: pageParams.value.pageNum,
pageSize: pageParams.value.pageSize
});
// 拼接当前页数据(分片加载)
stockList.value = [...stockList.value, ...res.data.list];
// 更新分页状态
pageParams.value.total = res.data.total;
// 判断是否加载完成(无更多数据)
if (stockList.value.length >= pageParams.value.total) {
pageParams.value.finished = true;
} else {
pageParams.value.pageNum += 1; // 页码+1,准备加载下一页
}
} catch (err) {
console.error('库存数据加载失败:', err);
} finally {
pageParams.value.loading = false;
}
};
// 页面初始化:加载第一页
onMounted(() => {
loadStockData();
});
// 页面触底:加载下一页(懒加载核心)
onReachBottom(() => {
loadStockData();
});
</script>
<template>
<view class="stock-list">
<!-- 库存列表:仅渲染已加载的分片数据 -->
<uni-list v-for="(item, index) in stockList" :key="index">
<uni-list-item
title="{{item.partName}}"
note="库存数量:{{item.stockNum}}"
show-arrow
/>
</uni-list>
<!-- 加载状态提示 -->
<view v-if="pageParams.loading" class="loading">加载中...</view>
<view v-if="pageParams.finished" class="finished">已加载全部库存</view>
</view>
</template>
额外优化(可选):可视区域渲染
如果单页加载20条仍有卡顿,可结合 uni-virtual-list(虚拟列表),仅渲染当前屏幕可视区域的库存项,进一步降低DOM渲染压力:
vue
<!-- 虚拟列表替代普通列表,仅渲染可视区域 -->
<uni-virtual-list
:height="800" // 列表高度
:item-height="80" // 每项高度
:list="stockList" // 数据源
>
<template v-slot:item="{ item }">
<uni-list-item
title="{{item.partName}}"
note="库存数量:{{item.stockNum}}"
/>
</template>
</uni-virtual-list>
核心要点
- 用
loading状态防止「触底重复请求」,用finished状态标记「无更多数据」; - 数据分片核心是「后端分页+前端分批请求」,仅加载当前页数据;
- 懒加载核心是「监听页面触底事件」,动态拼接数据,避免一次性渲染海量DOM。
总结
- 订单数据缓存:核心是「微信小程序本地存储+缓存策略」,区分临时/持久缓存,监听网络状态实现断网缓存、联网同步,保障低网环境操作流畅;
- 库存懒加载+数据分片:核心是「后端分页(数据分片)+ 前端触底懒加载」,结合loading/finished状态控制请求,可选虚拟列表进一步优化渲染性能,解决海量数据加载卡顿问题。
这两个方案既贴合UniApp+微信小程序的技术栈特性,也能在简历/面试中体现你对「性能优化」「边界场景(低网)」的思考,是汽配小程序这类ToB业务的核心优化点。