整体思路:在page/index.wxml页面引入自定义好的瀑布流组件,通过page/index.js初始化一个Paging的实例对象,这个对象是主要是用于通过api获取数据并记录当前请求状态的。onload时预加载指定count数量的数据,并将对象及对象返回的数据保存在data中,并通过瀑布流进行渲染。当触发触底事件时,通过调用对象的getMoreData方法获取下一批数据bin进行渲染。
page/index.wxml:
<!--由于标题头图基本不会改变,直接使用本地路径获取图片-->
<view class="spu-bottom">
<image src="../../img/xxx.png" class="spu-bottom-img"></image>
<!--根据lin-ui瀑布流组件要求,需通过generic:l-water-flow-item="xxx"
指定一个自定义组件,这个自定义组件即是要封装展示的每一块区域-->
<!--瀑布流组件内置columns-gap,表示两列数据之间的间隔。
为了避免与我们自定义样式类冲突,手动设置为0-->
<s-water-flow generic:l-water-flow-item="s-spu-preview" column-gap="0rpx"></s-water-flow>
</view>
自定义组件s-spu-preview:
/*json部分主要是引入价格组件,方便wxml调用
{
"component": true,
"usingComponents": {
"l-price": "../../miniprogram_npm/lin-ui/price/index"
}
-------------------------------------------------------------
js部分主要是接受一个data数据。
Component({
properties: {
data:Object
}....}
*/
<view class="container">
<!--头图-->
<image src="{{data.img}}" mode="widthFix" class="image"></image>
<!--标题-->
<view class="title">{{data.title}}</view>
<!--价格-->
<l-price color="#157658" value="{{data.price}}" l-class="price" size="32"></l-price>
<!--商品简介-->
<view class="subtitle">{{data.subtitle}}</view>
</view>
}
page/index.js:
//主页的js主要是用于数据的初始化、数据绑定、事件处理等。
page({
data:{
//用于渲染的数据,返回的实际上是一个对象,
//它具有一个items属性(Array),我们渲染的既是这个列表。
pagingData = null ,
//这个paging是一个实例化对象,每次需要通过它来获得新的数据
paging = Object
},
onLoad:async function(){
//Paging类主要进行数据的请求发起处理。
//其data属性用于保存返回的结果,getMoreData()方法用于请求下一批数据。
const paging = new Paging()
await paging.getMoreData()
this.setData({
pagingData:paging.data,
paging:paging
})
//当数据绑定完成后调用wx.lin.renderWaterFlow传入数据进行渲染
wx.lin.renderWaterFlow(this.data.pagingData.items,false)
},
onReachBottom:function(){
/*
renderWaterflow渲染数据时,refresh参数若为true,则会重新渲染一遍全部的数据。
而设置为false时则会保留已渲染过的,将只渲染指定的数据。
测试时设置true,重新渲染画面会有闪烁的情况,体验较差。
因此选择了通过slice选择选择新增的数据,并单独渲染这部分数据。
*/
const total = this.data.pagingData.total //分页加载api总体的数据量
let start = this.data.paging.count //当前已渲染过的数据量
//每页加载的数据量,在Paging类中定义,我们这里设置为3。
let end = start+3
//每次新增加3个有可能会超出最大数据量范围,通过三元表达式返回合适的结束值。
end = end>total?total:end
})
await this.data.paging.getMoreData() //再获取一批新的数据
this.setData({pagingI:this.data.paging.data}) //同样进行数据绑定
/*当api拥有的数据全部渲染完以后,通过上面start和end逻辑
可以看出来start和end是一定相等的。因此可以通过判断这两个值的关系决定是否完成了
全部数据的渲染(不再继续渲染新的数据)*/
//通过上面的的start和end,使用slice选择新增加的那部分数据并进行渲染
if(start!==end){
wx.lin.renderWaterFlow(this.data.pagingI.items.slice(start,end),false)
}
}
Paging类:
import {Http} from "./Http";
//这个Http只是个封装好api基地址、header认证信息等信息的wx.request请求
class Paging {
url;
start;
count;
locker;
data;
lastcount;
//构造函数
constructor(url,width = 3,start = 0){
/*由于api参数分别需要提供请求的起始序号和终止序号,
而没有分页加载指定的每页的数量,
因此我们需要通过每页加载数量来更新start和end。
*/
this.url = url;
this.start = start;
this.count = 0; //当前总共请了多少条数据
this.width = width; //每次请求几条数据
this.totalitems = 0; //是api中返回结果的total属性的值,全部数量
}
async getMoreData() {
if (this.count===this.totalitems && this.count!==0)
//当一次获取记录的条数count和api接口拥有的总体数量this.totalitems相同时,结束请求。
return ;
//判断当前请求锁的状态,如果this._getLocker()
//返回true表示上一次请求已结束,可以继续发起新的请求。
if (this._getLocker()) {
this.count = this.count+this.width; //更新count
if(this.count>this.totalitems && this.totalitems!==0){
//如过count已经超过了数据的最大请求数量,则令count等于这个最大请求数量
this.count=this.totalitems
}
const tmp_data = await Http.request({
url: `${this.url}?start=${this.start}&count=${this.count}`
});//发起新的请求
this.start = this.count;//更新start
if (~this.data) {
//data是一个array,当数组为空也就意味着这是第一次发起请求
this.data = tmp_data;
this.totalitems = tmp_data.total
}
else{
//之后再次发起请求则将请求结果append到数组中即可
this.data.append(tmp_data);
}
this._releaseLocker(); //请求完成,释放锁
return
}
return false
}
_getLocker(){
/*
获取锁的状态。若为锁的状态this.locker为false表示当前没有正在进行中的请求,
返回true表示允许发起新的请求,并将locker设置为true表示存在进行中的请求。
若锁的状态this.locker为true,则返回false表示已经有处理中的请求了,
不允许发出新的请求。
*/
if (this.locker){
return false
}
//锁状态为false,则另锁状态为true防止继续点击,并进行request
this.locker = true;
return true
}
_releaseLocker(){
//将锁状态置为false,代表上一次请求已经完成,允许发出新的请求。
this.locker = false
}
}
export {Paging}
只是做了个大致的效果,其他的触底状态显示、触发距离等未做优化,大致效果如下:
Jietu20191208-171535.gif










网友评论