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

