微信小程序列表加载更多
概述
详细
一、前言
基于小程序开发的列表加载更多例子。
二、运行效果
运行效果(演示的小视频,点击播放即可)
三、实现过程
总体思路如何:
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: {
}
})
三、项目结构
四、其他补充
暂时没有
注:本文著作权归作者,由demo大师发表,拒绝转载,转载需要作者授权
微信小程序列表加载更多的更多相关文章
- [转]微信小程序之加载更多(分页加载)实例 —— 微信小程序实战系列(2)
本文转自;http://blog.csdn.net/michael_ouyang/article/details/56846185 loadmore 加载更多(分页加载) 当用户打开一个页面时,假设后 ...
- 微信小程序之加载更多(分页加载)实例
业务需求: 列表滚动到底部时,继续往上拉,加载更多内容 必备参数: (1)pageindex: 1 //第几次加载 (2)callbackcount: 15 //需要返回数据的个数 其他参数: 根据接 ...
- 微信小程序,加载更多
html <!-- 头部 --> <view class='tab'> <view class="tab-new {{selected_new?'active' ...
- 微信小程序分包加载实战
"离线包"机制 微信小程序采用的是类似离线包加载方案,以转转小程序为例,当用户第一次打开时会先下载好所有代码,然后再加载页面:当用户再次进入转转小程序时,会直接使用已下载的代码,省 ...
- 微信小程序 - 分包加载
小程序开发大家都知道,对主包的大小进行了限制,从最初的1M变成了现再的2M,一般情况下是够用了:但是偶尔可能会出现超出2M的可能,我们可以对小程序进行分包加载. 1.小程序分包加载 a. 某些情况下, ...
- 微信小程序 无限加载 上拉加载更多
加载更多,其实就是再次向接口发送请求,把返回的数据,追加到渲染页面的数组里的过程,具体实现实例如下: demo.js // pages/project/project.js const app = g ...
- 微信小程序分页加载列表
1.假设加载的数据为 2.wxml <view class="page"> <view class="page__bd"> <vi ...
- 微信小程序 图片加载失败处理方法
微信小程序 官方文档对image 媒体组件加载失败 没有太多的解释,使用中出现了几个小问题,今天抽空记录一下 WXML: <image class="userinfo-avatar&q ...
- 微信小程序室内地图导航开发-微信小程序JS加载esmap地图
一.在微信小程序里显示室内三维地图 需要满足的两个条件 调用ESMap室内地图需要用到小程序web-view组件,想要通过 web-view 调用ESMap室内地图需要满足以下 2 个条件: 1. 小 ...
随机推荐
- 函数中的 arguments 对象
JavaScript函数具有像数组一样的对象,这些对象称为arguments,与传递给函数的参数相对应.传递给JavaScript函数的所有参数都可以使用arguments对象来引用. 现在我们开始学 ...
- Circuit level-shifts ac signals
AC signals can emanate from many sources, and many of these sources are incompatible with the most p ...
- 单端通用ISM频段接收器 Si4313
Si4313芯片是单端通用ISM频段接收器,工作频率为240-960MHz,可编程接收频率带宽为2.6-260kHz,接收灵敏度为-118dBm,数据速率为0.2-128kb/s,采用FSK.GFSK ...
- 15KW电动机380V及220V时的电流分别为多少
15KW电动机380V及220V时的电流分别为多少 当用电电压为380V时:P=UICOSφ/1.72,此时电流为: I=15KW/380V/0.83(COSφ,功率因数)/1.72x1000=27. ...
- SQL中关于where后面不能放聚合函数(如sum等)的解决办法
我们在编写较为复杂的SQL语句的时候,常常会遇到需要将sum()放到where后面作为条件查询,事实证明这样是无法执行的,执行会报[此处不允许使用分组函数]异常. 那么如何解决呢,使用HAVING关键 ...
- 海思hi3518 移植live555 实现H264的RTSP播放
用海思的交叉编译工具交叉编译live555 ,结合海思例子venc中的H264部分,完成RTSP的视频数据发布. 用vlc可以播放,但是实时性比较差,慢了5秒
- [Android Studio] Android Studio快速定位当前打开的文件在哪个目录(package)下
转载自:http://blog.csdn.net/hyr83960944/article/details/38067499 在Eclipse中有一个很好的功能,就是比如我打开一个AActivity,左 ...
- Android 4.4 KitKat升级率已经接近18%(2014-07-09 07:29)
腾讯数码讯(编 译:张秀梅)按照惯例, 每个月的第一个星期的星期一谷歌都会发布最新一期Android版本分布图.从去年十月末谷歌发布Android 4.4 KitKat以来,截止到目前为止Androi ...
- mysql中避免使用保留字和关键字做列的名字
设计数据表时,应尽量避免使用MySQL的关键字和保留字作为表名或列名. 比如key和keys为保留字,如果不小心使用关键字或者保留字作为列名字,执行下面的语句会出现语法错误: select * fro ...
- 如何把rc.local里执行的shell脚本的日志内容放到其他位置
rc.local的日志内容默认是/var/log/boot.log /etc/rc.d/rc.local文件的文件头是#!/bin/sh ,我们把这修改成#!/bin/sh -x,这样系统启动后就会把 ...