概述
基于小程序开发的列表加载更多例子。
详细
一、前言
基于小程序开发的列表加载更多例子。
二、运行效果
运行效果(演示的小视频,点击播放即可)
三、实现过程
总体思路如何:
1、通过scroll-view组件提供的bindscroll方法监控滚动的时候是否距离底部在40px内,如果小于40px则触发加载更多方法(见完整代码index.js里的bindscroll方法)
2、通过使用发现很多时候服务返回数据太快了,没有加载等待的过程,显的不自然,所以在loadMore方法里通过setTimeout来保证至少有333毫秒的加载时间(见完整代码index.js里的loadMore方法)
3、实际使用中又发现一个问题,上滑到底部会重复触发加载更多方法导致重复的网络请求。通过记录上次加载时间lastRequestTime,保证两次网络请求的间隔大于1秒(见完整代码index.js里的fetchList方法),这样就能避免重复调用加载更多的问题
备注:demo代码里的网络请求wx.requestTest方法是为了显示效果,所以写了个模拟的请求方法,实际使用可替换为wx.request对接自己项目的服务
具体实现如下:
1、创建小程序,点击下图里框起来的位置,创建小程序
2、在app.js里添加网络模拟方法
let serverData = []; for(let i = 1; i < 25; i++){ serverData.push({id:i, name:i}) } App({ onLaunch: function () { wx.requestTest = ({data:{page,size},success}) => { setTimeout( () => { //模拟网络返回请求 let res = { data:{ data:{ rows: serverData.slice((page - 1) * size, size + (page - 1) * size) }, result: true, } } console.log(res) success(res) },1000//模拟网络延迟 ) } }, globalData: { } })
3、增加和pages同层级的components文件夹,在里面创建Loading文件夹,并在下面创建以下文件
//loading.js Component({ data: { }, properties: { visible: {//loading效果是否显示 type: Boolean, value: false//默认不显示 }, }, }) //loading.json { "component": true,//表示是组件 "usingComponents": {} } //loading.wxss .loadmore { 100%; height: 0rpx; display: flex; align-items: center; justify-content: center; padding-top:24rpx; transition: all 200ms linear; } .loadmore.visible { height: 80rpx; } .my-loading:after { content: " "; display: block; 26px; height: 26px; margin: 1px; border-radius: 50%; border: 2px solid #FFD800; border-color: #fff transparent #FFD800 transparent; animation: lds-dual-ring 1.2s linear infinite; } @keyframes lds-dual-ring { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } //loading.wxml <view class="loadmore {{visible && 'visible'}}"> <view class="my-loading" wx:if="{{visible}}"></view> </view>
4、修改pages/index文件夹下各文件如下
//index.json { "navigationBarTitleText": "首页", "usingComponents": { "loading": "/components/Loading/loading"//引用组件 } } //index.js const app = getApp() let loadingMore = false let lastScollTop = 0; let lastRequestTime = 0; Page({ data: { list: [], hasMore: true,//列表是否有数据未加载 page: 1, size: 8,//每页8条数据 scrollYHeight: 0,//scroll-view高度 }, bindscroll: function (e) { const { scrollHeight, scrollTop } = e.detail; const { scrollYHeight, hasMore } = this.data; //如果当前没有加载中且列表还有数据未加载,且页面滚动到距离底部40px内 if (!loadingMore && hasMore && (scrollHeight - scrollYHeight - scrollTop < 40) && lastScollTop <= scrollTop) { this.loadMore() } lastScollTop = scrollTop }, loadMore: function () { const { page, hasMore } = this.data; if (!hasMore || loadingMore) return; loadingMore = true setTimeout( () => { this.fetchList(page + 1, () => { loadingMore = false; }) }, 333 ) }, fetchList: function (page, cb) { let nowRequestTime = (new Date()).getTime(); //限制两次网络请求间隔至少1秒 if (nowRequestTime - lastRequestTime < 1000) { if (cb) cb(); return; } lastRequestTime = nowRequestTime //这里wx.requestTest实际使用时换成wx.request //wx.requestTest定义见app.js wx.requestTest({ url: "testUrl", header: { 'Authorization': wx.getStorageSync('token') }, data: { page, size: this.data.size, }, success: (res) => { if (res.data && res.data.result) { let list = res.data.data.rows || []; if (list.length == 0) { this.setData({ hasMore: false, page, }) } else { this.setData({ list: this.data.list.concat(list), hasMore: list.length == this.data.size, page, }) } } else { wx.showToast({ title: res.data ? res.data.message : "列表加载失败", icon: 'none', duration: 1000 }) } if (cb) { cb() } }, fail: () => { wx.showToast({ title: "列表加载失败", icon: 'none', duration: 1000 }) if (cb) { cb() } } }) }, onReady: function () { wx.getSystemInfo({ success: ({ windowHeight }) => { this.setData({ scrollYHeight: windowHeight })//设置scrill-view组件的高度为屏幕高度 } }) }, onLoad: function () { this.fetchList(1)//加载第一页数据 } }) //index.wxml <scroll-view scroll-y style="height:{{scrollYHeight}}px" scroll-top="{{scrollTop}}" bindscroll="bindscroll"> <view class="item" wx:for="{{list}}" wx:key="id" wx:for-index="idx" > {{item.name}} </view> <loading visible="{{hasMore}}"></loading> </scroll-view> //index.css .item { 750rpx; height: 200rpx; font-size: 40rpx; color: black; position: relative; display: flex; align-items: center; justify-content: center; } .item::after{ content: ""; position: absolute; left: 0; right: 0; bottom: 0; border-bottom: 1rpx solid #eeeeee; }
此时运行程序,可查看效果。
整体代码:
//index.js const app = getApp() let loadingMore = false let lastScollTop = 0; let lastRequestTime = 0; Page({ data: { list: [], hasMore: true,//是否有数据未加载 page: 1, size: 8, scrollYHeight: 0, }, bindscroll: function (e) { const { scrollHeight, scrollTop } = e.detail; const { scrollYHeight, hasMore } = this.data; //如果当前没有加载中且列表还有数据未加载,且页面滚动到距离底部40px内 if (!loadingMore && hasMore && (scrollHeight - scrollYHeight - scrollTop < 40) && lastScollTop <= scrollTop) { this.loadMore() } lastScollTop = scrollTop }, loadMore: function () { const { page, hasMore } = this.data; if (!hasMore || loadingMore) return; loadingMore = true setTimeout( () => { this.fetchList(page + 1, () => { loadingMore = false; }) }, 333 ) }, fetchList: function (page, cb) { let nowRequestTime = (new Date()).getTime(); if (nowRequestTime - lastRequestTime < 1000) { if (cb) cb(); return; } lastRequestTime = nowRequestTime //这里wx.requestTest实际使用时换成wx.request //wx.requestTest定义见app.js wx.requestTest({ url: "testUrl", header: { 'Authorization': wx.getStorageSync('token') }, data: { page, size: this.data.size, }, success: (res) => { if (res.data && res.data.result) { let list = res.data.data.rows || []; if (list.length == 0) { if(page == 1){ this.setData({ hasMore: false, page, list: [] }) }else { this.setData({ hasMore: false, page, }) } } else { this.setData({ list: this.data.list.concat(list), hasMore: list.length == this.data.size, page, }) } } else { wx.showToast({ title: res.data ? res.data.message : "列表加载失败", icon: 'none', duration: 1000 }) } if (cb) { cb() } }, fail: () => { wx.showToast({ title: "列表加载失败", icon: 'none', duration: 1000 }) if (cb) { cb() } } }) }, onReady: function () { const { windowWidth, ratio } = app.globalData wx.getSystemInfo({ success: ({ windowHeight, pixelRatio }) => { this.setData({ scrollYHeight: windowHeight }) } }) }, onLoad: function () { this.fetchList(1) } }) //index.wxml <scroll-view scroll-y style="height:{{scrollYHeight}}px" scroll-top="{{scrollTop}}" bindscroll="bindscroll"> <view class="item" wx:for="{{list}}" wx:key="id" wx:for-index="idx" > {{item.name}} </view> <loading visible="{{hasMore}}"></loading> </scroll-view> //index.css .item { 750rpx; height: 200rpx; font-size: 40rpx; color: black; position: relative; display: flex; align-items: center; justify-content: center; } .item::after{ content: ""; position: absolute; left: 0; right: 0; bottom: 0; border-bottom: 1rpx solid #eeeeee; } //app.js let serverData = []; for(let i = 1; i < 25; i++){ serverData.push({id:i, name:i}) } App({ onLaunch: function () { wx.requestTest = ({data:{page,size},success}) => { setTimeout( () => { //模拟网络返回请求 let res = { data:{ data:{ rows: serverData.slice((page - 1) * size, size + (page - 1) * size) }, result: true, } } console.log(res) success(res) },1000//模拟网络延迟 ) } }, globalData: { } })
三、项目结构
四、其他补充
暂时没有