整體思路:在page/index.wxml頁(yè)面引入自定義好的瀑布流組件,通過page/index.js初始化一個(gè)Paging的實(shí)例對(duì)象,這個(gè)對(duì)象是主要是用于通過api獲取數(shù)據(jù)并記錄當(dāng)前請(qǐng)求狀態(tài)的。onload時(shí)預(yù)加載指定count數(shù)量的數(shù)據(jù),并將對(duì)象及對(duì)象返回的數(shù)據(jù)保存在data中,并通過瀑布流進(jìn)行渲染。當(dāng)觸發(fā)觸底事件時(shí),通過調(diào)用對(duì)象的getMoreData方法獲取下一批數(shù)據(jù)bin進(jìn)行渲染。
page/index.wxml:
<!--由于標(biāo)題頭圖基本不會(huì)改變,直接使用本地路徑獲取圖片-->
<view class="spu-bottom">
<image src="../../img/xxx.png" class="spu-bottom-img"></image>
<!--根據(jù)lin-ui瀑布流組件要求,需通過generic:l-water-flow-item="xxx"
指定一個(gè)自定義組件,這個(gè)自定義組件即是要封裝展示的每一塊區(qū)域-->
<!--瀑布流組件內(nèi)置columns-gap,表示兩列數(shù)據(jù)之間的間隔。
為了避免與我們自定義樣式類沖突,手動(dòng)設(shè)置為0-->
<s-water-flow generic:l-water-flow-item="s-spu-preview" column-gap="0rpx"></s-water-flow>
</view>
自定義組件s-spu-preview:
/*json部分主要是引入價(jià)格組件,方便wxml調(diào)用
{
"component": true,
"usingComponents": {
"l-price": "../../miniprogram_npm/lin-ui/price/index"
}
-------------------------------------------------------------
js部分主要是接受一個(gè)data數(shù)據(jù)。
Component({
properties: {
data:Object
}....}
*/
<view class="container">
<!--頭圖-->
<image src="{{data.img}}" mode="widthFix" class="image"></image>
<!--標(biāo)題-->
<view class="title">{{data.title}}</view>
<!--價(jià)格-->
<l-price color="#157658" value="{{data.price}}" l-class="price" size="32"></l-price>
<!--商品簡(jiǎn)介-->
<view class="subtitle">{{data.subtitle}}</view>
</view>
}
page/index.js:
//主頁(yè)的js主要是用于數(shù)據(jù)的初始化、數(shù)據(jù)綁定、事件處理等。
page({
data:{
//用于渲染的數(shù)據(jù),返回的實(shí)際上是一個(gè)對(duì)象,
//它具有一個(gè)items屬性(Array),我們渲染的既是這個(gè)列表。
pagingData = null ,
//這個(gè)paging是一個(gè)實(shí)例化對(duì)象,每次需要通過它來(lái)獲得新的數(shù)據(jù)
paging = Object
},
onLoad:async function(){
//Paging類主要進(jìn)行數(shù)據(jù)的請(qǐng)求發(fā)起處理。
//其data屬性用于保存返回的結(jié)果,getMoreData()方法用于請(qǐng)求下一批數(shù)據(jù)。
const paging = new Paging()
await paging.getMoreData()
this.setData({
pagingData:paging.data,
paging:paging
})
//當(dāng)數(shù)據(jù)綁定完成后調(diào)用wx.lin.renderWaterFlow傳入數(shù)據(jù)進(jìn)行渲染
wx.lin.renderWaterFlow(this.data.pagingData.items,false)
},
onReachBottom:function(){
/*
renderWaterflow渲染數(shù)據(jù)時(shí),refresh參數(shù)若為true,則會(huì)重新渲染一遍全部的數(shù)據(jù)。
而設(shè)置為false時(shí)則會(huì)保留已渲染過的,將只渲染指定的數(shù)據(jù)。
測(cè)試時(shí)設(shè)置true,重新渲染畫面會(huì)有閃爍的情況,體驗(yàn)較差。
因此選擇了通過slice選擇選擇新增的數(shù)據(jù),并單獨(dú)渲染這部分?jǐn)?shù)據(jù)。
*/
const total = this.data.pagingData.total //分頁(yè)加載api總體的數(shù)據(jù)量
let start = this.data.paging.count //當(dāng)前已渲染過的數(shù)據(jù)量
//每頁(yè)加載的數(shù)據(jù)量,在Paging類中定義,我們這里設(shè)置為3。
let end = start+3
//每次新增加3個(gè)有可能會(huì)超出最大數(shù)據(jù)量范圍,通過三元表達(dá)式返回合適的結(jié)束值。
end = end>total?total:end
})
await this.data.paging.getMoreData() //再獲取一批新的數(shù)據(jù)
this.setData({pagingI:this.data.paging.data}) //同樣進(jìn)行數(shù)據(jù)綁定
/*當(dāng)api擁有的數(shù)據(jù)全部渲染完以后,通過上面start和end邏輯
可以看出來(lái)start和end是一定相等的。因此可以通過判斷這兩個(gè)值的關(guān)系決定是否完成了
全部數(shù)據(jù)的渲染(不再繼續(xù)渲染新的數(shù)據(jù))*/
//通過上面的的start和end,使用slice選擇新增加的那部分?jǐn)?shù)據(jù)并進(jìn)行渲染
if(start!==end){
wx.lin.renderWaterFlow(this.data.pagingI.items.slice(start,end),false)
}
}
Paging類:
import {Http} from "./Http";
//這個(gè)Http只是個(gè)封裝好api基地址、header認(rèn)證信息等信息的wx.request請(qǐng)求
class Paging {
url;
start;
count;
locker;
data;
lastcount;
//構(gòu)造函數(shù)
constructor(url,width = 3,start = 0){
/*由于api參數(shù)分別需要提供請(qǐng)求的起始序號(hào)和終止序號(hào),
而沒有分頁(yè)加載指定的每頁(yè)的數(shù)量,
因此我們需要通過每頁(yè)加載數(shù)量來(lái)更新start和end。
*/
this.url = url;
this.start = start;
this.count = 0; //當(dāng)前總共請(qǐng)了多少條數(shù)據(jù)
this.width = width; //每次請(qǐng)求幾條數(shù)據(jù)
this.totalitems = 0; //是api中返回結(jié)果的total屬性的值,全部數(shù)量
}
async getMoreData() {
if (this.count===this.totalitems && this.count!==0)
//當(dāng)一次獲取記錄的條數(shù)count和api接口擁有的總體數(shù)量this.totalitems相同時(shí),結(jié)束請(qǐng)求。
return ;
//判斷當(dāng)前請(qǐng)求鎖的狀態(tài),如果this._getLocker()
//返回true表示上一次請(qǐng)求已結(jié)束,可以繼續(xù)發(fā)起新的請(qǐng)求。
if (this._getLocker()) {
this.count = this.count+this.width; //更新count
if(this.count>this.totalitems && this.totalitems!==0){
//如過count已經(jīng)超過了數(shù)據(jù)的最大請(qǐng)求數(shù)量,則令count等于這個(gè)最大請(qǐng)求數(shù)量
this.count=this.totalitems
}
const tmp_data = await Http.request({
url: `${this.url}?start=${this.start}&count=${this.count}`
});//發(fā)起新的請(qǐng)求
this.start = this.count;//更新start
if (~this.data) {
//data是一個(gè)array,當(dāng)數(shù)組為空也就意味著這是第一次發(fā)起請(qǐng)求
this.data = tmp_data;
this.totalitems = tmp_data.total
}
else{
//之后再次發(fā)起請(qǐng)求則將請(qǐng)求結(jié)果append到數(shù)組中即可
this.data.append(tmp_data);
}
this._releaseLocker(); //請(qǐng)求完成,釋放鎖
return
}
return false
}
_getLocker(){
/*
獲取鎖的狀態(tài)。若為鎖的狀態(tài)this.locker為false表示當(dāng)前沒有正在進(jìn)行中的請(qǐng)求,
返回true表示允許發(fā)起新的請(qǐng)求,并將locker設(shè)置為true表示存在進(jìn)行中的請(qǐng)求。
若鎖的狀態(tài)this.locker為true,則返回false表示已經(jīng)有處理中的請(qǐng)求了,
不允許發(fā)出新的請(qǐng)求。
*/
if (this.locker){
return false
}
//鎖狀態(tài)為false,則另鎖狀態(tài)為true防止繼續(xù)點(diǎn)擊,并進(jìn)行request
this.locker = true;
return true
}
_releaseLocker(){
//將鎖狀態(tài)置為false,代表上一次請(qǐng)求已經(jīng)完成,允許發(fā)出新的請(qǐng)求。
this.locker = false
}
}
export {Paging}
只是做了個(gè)大致的效果,其他的觸底狀態(tài)顯示、觸發(fā)距離等未做優(yōu)化,大致效果如下:

Jietu20191208-171535.gif