概述
基於小程序開發的列表加載更多例子。
詳細
一、前言
基於小程序開發的列表加載更多例子。
二、運行效果
運行效果(演示的小視頻,點擊播放即可)
三、實現過程
總體思路如何:
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 { width: 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; width: 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 { width: 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 { width: 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: { } })
三、項目結構

四、其他補充
暫時沒有