11 KiB
11 KiB
分页加载功能实现说明
📋 功能概述
已为价格查询页面实现触底自动加载更多功能,解决了只能展示 100 条数据的限制。
✨ 核心特性
1. 智能分页
- 首屏加载 20 条数据(快速响应)
- 每次滚动到底部自动加载下一页(20 条)
- 支持加载任意数量的数据(4000+ 条无压力)
2. 加载状态提示
- 加载中:显示圆形加载动画 + "加载中..." 文字
- 继续滚动:显示蓝色闪烁提示"继续滚动加载更多"
- 已加载全部:显示灰色"已加载全部数据"
3. 数据统计
- 实时显示"共找到 X 条结果,已加载 Y 条"
- 用户清楚知道当前加载进度
4. 防重复加载
- 智能判断:正在加载时不重复触发
- 自动检测:已加载全部数据后不再请求
🎯 实现方案
方案选择:触底加载 + 状态提示
优势:
- ✅ 用户体验好,无需手动点击
- ✅ 符合移动端操作习惯
- ✅ 代码简洁,维护方便
- ✅ 性能优秀,按需加载
工作流程:
用户查询 → 加载第1页(20条)
↓
滚动查看数据
↓
触底触发 onReachBottom()
↓
自动加载第2页(20条)
↓
追加到现有列表
↓
重复直到加载全部数据
🔧 技术实现
1. 状态管理 (index.js:5-83)
data: {
// 分页参数
currentPage: 1, // 当前页码
pageSize: 20, // 每页数量(优化为20,首屏更快)
hasMore: true, // 是否还有更多数据
loadingMore: false, // 加载更多状态
// 数据列表
priceList: [], // 价格数据列表(累加)
total: 0, // 总数据量
}
2. 首次查询 (index.js:216-301)
async onSearch() {
// 重置分页状态
this.setData({
currentPage: 1,
priceList: [],
hasMore: true
})
// 请求第1页数据
const searchParams = {
region: selectedRegion,
page: 1,
pageSize: 20 // 关键:使用分页参数
}
const searchResult = await api.searchPrices(searchParams)
this.processSearchResult(searchResult, statsResult)
}
3. 触底加载 (index.js:348-402)
async onReachBottom() {
const { loading, loadingMore, hasMore, searched, total, priceList } = this.data
// 防重复加载
if (loading || loadingMore || !hasMore || !searched) {
return
}
// 已加载全部数据
if (priceList.length >= total) {
this.setData({ hasMore: false })
return
}
// 加载下一页
this.setData({
loadingMore: true,
currentPage: this.data.currentPage + 1
})
const searchParams = {
region: this.data.selectedRegion,
page: this.data.currentPage, // 下一页页码
pageSize: 20
}
const searchResult = await api.searchPrices(searchParams)
this.processSearchResult(searchResult, { data: this.data.stats })
}
4. 数据处理 (index.js:306-343)
processSearchResult(searchResult, statsResult) {
const priceList = searchResult.data || []
const total = searchResult.total || 0
// 格式化数据
const formattedList = priceList.map(item => ({
...item,
price_date_str: formatDate(item.price_date)
}))
// 判断是否还有更多数据
const hasMore = formattedList.length >= this.data.pageSize &&
this.data.priceList.length + formattedList.length < total
// 累加数据
const newList = this.data.currentPage === 1
? formattedList
: [...this.data.priceList, ...formattedList]
this.setData({
priceList: newList,
total,
hasMore,
loadingMore: false
})
}
5. UI 状态 (index.wxml:129-145)
<!-- 加载更多状态 -->
<view class="load-more" wx:if="{{priceList.length > 0}}">
<!-- 加载中 -->
<view class="loading-more" wx:if="{{loadingMore}}">
<t-loading theme="circular" size="40rpx" text="加载中..."></t-loading>
</view>
<!-- 没有更多数据 -->
<view class="no-more" wx:elif="{{!hasMore}}">
<text>已加载全部数据</text>
</view>
<!-- 继续滚动提示 -->
<view class="scroll-hint" wx:else>
<text>继续滚动加载更多</text>
</view>
</view>
6. 样式动画 (index.wxss:223-260)
.load-more {
padding: 32rpx 0;
text-align: center;
border-top: 1rpx solid #f0f0f0;
}
.scroll-hint {
color: #0052D9;
font-size: 26rpx;
animation: pulse 2s ease-in-out infinite; /* 呼吸灯效果 */
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.6; }
}
7. 页面配置 (index.json:1-5)
{
"onReachBottomDistance": 50 // 距离底部50px时触发
}
📊 使用示例
场景 1:查询到 4000 条数据
用户操作:选择"昆明"地区 → 点击"查询价格"
系统行为:
1. 加载第1页(20条)→ 显示"共找到 4000 条结果,已加载 20 条"
2. 用户滚动到底部 → 自动加载第2页(20条)
3. 显示"共找到 4000 条结果,已加载 40 条"
4. 重复直到加载完全部 4000 条数据
5. 显示"已加载全部数据"
场景 2:数据不足 20 条
用户操作:选择"大理"地区 → 点击"查询价格"
系统行为:
1. 加载第1页(15条)→ 显示"共找到 15 条结果,已加载 15 条"
2. 直接显示"已加载全部数据"(不会触发加载更多)
🎨 UI 效果
加载状态展示
┌─────────────────────────────┐
│ 共找到 4000 条结果,已加载 60条 │
├─────────────────────────────┤
│ 价格数据卡片 1 │
│ 价格数据卡片 2 │
│ 价格数据卡片 3 │
│ ... │
├─────────────────────────────┤
│ 继续滚动加载更多 │ ← 蓝色闪烁提示
└─────────────────────────────┘
加载中状态
┌─────────────────────────────┐
│ 共找到 4000 条结果,已加载 80条 │
├─────────────────────────────┤
│ 价格数据卡片 ... │
├─────────────────────────────┤
│ 🔄 加载中... │ ← 加载动画
└─────────────────────────────┘
已加载全部
┌─────────────────────────────┐
│ 共找到 4000 条结果,已加载 4000条│
├─────────────────────────────┤
│ 价格数据卡片 ... │
├─────────────────────────────┤
│ 已加载全部数据 │ ← 灰色提示
└─────────────────────────────┘
🚀 性能优化
1. 首屏加载优化
- 从 100 条减少到 20 条(首屏速度提升 5 倍)
- 用户感知响应更快
2. 按需加载
- 只加载用户需要查看的数据
- 节省流量和内存
3. 防抖处理
- 避免重复请求同一页数据
- 减少服务器压力
4. 累加策略
- 数据追加而非替换(避免列表闪烁)
- 保持滚动位置
🔍 调试技巧
查看加载日志
// 在控制台查看分页信息
console.log('当前页:', this.data.currentPage)
console.log('已加载:', this.data.priceList.length)
console.log('总数:', this.data.total)
console.log('还有更多:', this.data.hasMore)
模拟触底加载
在微信开发者工具中:
- 点击"调试器" → "Console"
- 滚动页面到底部
- 查看"触底加载更多..."日志
- 观察网络请求
/api/prices/search?page=2
测试边界场景
// 场景 1:数据量正好是 pageSize 的倍数
total = 40, pageSize = 20 → 应加载2页
// 场景 2:数据量不足一页
total = 15, pageSize = 20 → 应加载1页,显示"已加载全部"
// 场景 3:数据量非常大
total = 10000, pageSize = 20 → 应加载500页
📝 代码变更清单
已修改文件
-
- 新增
currentPage,pageSize,hasMore,loadingMore状态 - 重构
onSearch()支持分页 - 新增
onReachBottom()触底加载 - 新增
processSearchResult()统一数据处理
- 新增
-
- 新增"加载更多状态"UI(3种状态)
- 优化列表头部文案(显示已加载数量)
-
- 新增
.load-more样式 - 新增
.scroll-hint呼吸灯动画
- 新增
-
- 新增
onReachBottomDistance: 50配置
- 新增
🎯 后续优化建议
1. 虚拟列表(适用于超大数据量)
如果数据量超过 10000 条,建议使用虚拟列表:
// 只渲染可见区域的数据
// 微信小程序可使用 recycle-view 组件
2. 数据缓存
// 缓存已加载的数据,避免重复请求
const cacheKey = `prices_${region}_${material}_${page}`
3. 预加载(更激进的策略)
// 在滚动到 80% 时预加载下一页
// 用户感觉不到加载延迟
4. 加载更多按钮(可选)
为不喜欢滚动的用户提供备选方案:
<t-button wx:if="{{hasMore}}" bindtap="onLoadMore">
加载更多
</t-button>
❓ 常见问题
Q1: 为什么不一次性加载所有数据?
A:
- ❌ 性能问题:4000 条数据会占用大量内存,导致页面卡顿
- ❌ 网络问题:一次性加载会消耗大量流量,等待时间长
- ❌ 用户体验:首屏加载慢,用户感知差
✅ 分页加载:按需加载,快速响应,流畅体验
Q2: 如何调整每页加载的数量?
A: 修改 index.js:70
pageSize: 20, // 改为你想要的数量,建议 10-50
Q3: 触底加载不生效怎么办?
检查清单:
- ✅ 确认
onReachBottomDistance已配置 - ✅ 确认
hasMore: true(还有数据) - ✅ 确认
searched: true(已执行查询) - ✅ 确认没有其他元素遮挡底部(如 TabBar)
Q4: 如何禁用自动加载,改用手动点击?
A: 删除 onReachBottom() 方法,改用按钮:
<t-button wx:if="{{hasMore}}" bindtap="onLoadMore">
加载更多
</t-button>
📚 相关文档
实现日期:2026-01-07 版本:v1.0 状态:✅ 已完成并测试