實現方式
在智能小程序的開發過程中,經常會遇到頁面列表數量較多的情況,此時可以通過【分頁】加載數據,並監聽頁面滑動到底部時觸發【上滑加載更多】,從而增加頁面首屏渲染速度。 想要實現這種分頁展示數據,上滑加載更多的效果,主要有以下幾種方式:
- 使用 view自定義信息流組件 + onReachBottom
- 使用 scroll-view + bindscrolltolower
- 使用 smart-ui 中的feed信息流組件
- 使用 swiper + scroll-view 自定義信息流組件 + bindscrolltolower
- 使用 swiper + view 自定義信息流組件 + onReachBottom
- 使用 page-feed信息流模版
以下是具體方案中會使用到的組件或api:
組件/ api名稱 | 描述 | 類型 |
---|---|---|
scroll-view | 滾動區域組件 | 原生組件 |
view | 視圖組件 | 原生組件 |
tabs | 標籤欄組件 | 原生組件 |
tabs-item | 標籤欄子項組件 | 原生組件 |
swiper | 滑塊視圖容器 | 原生組件 |
swiper-item | 滑塊視圖子項組件 | 原生組件 |
feed | 信息流組件 | 擴展組件(smart-ui) |
spin | 加載狀態組件 | 擴展組件(smart-ui) |
page-status | 頁面狀態組件 | 擴展組件(smart-ui) |
page-feed | 信息流模版 | 頁面模版 |
createSelectorQuery | 返回一個 SelectorQuery 對象實例 | swan api |
onReachBottom | 頁面加載到底部時觸發 | 頁面事件函數 |
針對不同的場景,可以使用不同的方式實現,下面我從四個常見的場景進行具體分析。
分場景描述
【場景一】
- 場景特徵:頁面整體由一塊滾動區域(A) + 上滑加載更多(B)組成
- 場景展示:
- 實現方案:
方案一:採用 page-feed 信息流模版實現
用百度開發者工具打開
關鍵代碼:
<!-- feed 信息流 組件 -->
<smt-feed
s-if="!showPageStatus"
class="smt-feed pull-down-refresh"
pull-to-refresh
bind:refresh="onRefresh"
bind:scrolltolower="scrollToLower"
bind:scroll="scrollHandler"
text="{{text}}"
>
<!-- feed 信息流子組件 -->
<smt-feed-item
s-for="item in list"
theme="{{item.theme}}"
content="{{item.content}}"
video="{{item.video}}"
status="{{item.status}}"
bindfeeditemtap="feedItemTap"
>
</smt-feed-item>
<!-- 上滑加載更多組件 -->
<smt-spin s-if="loaded" status="{{status}}" bind:tap="reload"></smt-spin>
</smt-feed>
<!-- 頁面狀態組件 -->
<smt-page-status
s-if="showPageStatus"
class="content-loading"
icon="{{loadingIcon}}"
loading="{{loading}}"
showBtn="{{loadingBtn}}"
title="{{loadingTitle}}"
loadingTitle="正在加載..."
bind:smtreloading="reloadPage">
</smt-page-status>
方案優勢:
拿來即用,避免重複造輪子。npm i @smt-ui-template/page-feed 直接安裝就可以使用,涵蓋了【信息流圖片懶加載】【下拉刷新頁面】【上滑加載更多】【無網絡、無內容、頁面加載中狀態頁】等功能。
方案二:採用 view 自定義信息流組件 + onReachBottom
用百度開發者工具打開
關鍵代碼:
1)配置頁面高度爲整個視口高度
<!-- 需要配置頁面高度爲整個視口高度 -->
<view class="page-container" style="height: 100vh">
<!-- 自定義信息流組件 -->
<list list="{{list}}"></list>
<!-- 自定義上滑加載更多組件 -->
<loading-more status="{{status}}" bind:tap="reload"></loading-more>
</view>
2)頁面 onLoad 時獲取首屏數據
data: {
// 初始化頁碼爲0,表示第一頁
count: 0
},
onLoad() {
this.fetchFirstPageData();
},
/**
* 渲染首屏數據
*/
async fetchFirstPageData() {
// 請求首頁數據
const {data: list} = await this.fetchData();
this.syncSetData(this, {
// 渲染信息流組件數據
list,
// 頁碼+1,用於請求下一屏數據時傳遞頁碼
count: this.data.count + 1,
// 加載狀態組件,status: 1表示“加載中”
status: 1
});
}
3)監聽頁面onReachBottom時觸發加載更多內容
/**
* 監聽頁面滾動到底部,觸發加載更多
*/
onReachBottom() {
this.scrollToLower();
},
/**
* 上拉觸底加載更多
*/
async scrollToLower() {
const {data: list} = await this.fetchData();
// 模擬請求第四頁數據時,加載組件展示“加載異常”
const fail = this.data.count === 3;
// 模擬請求第五頁數據時,加載組件展示“沒有更多內容”
const end = this.data.count === 5;
if (fail || end) {
await this.syncSetData(this, {
// status: 3,加載組件展示“加載失敗,請點擊重新加載”
// status: 2,加載組件展示“已經到底啦“
status: fail ? 3 : 2
});
return;
}
await this.syncSetData(this, {
// 當前數據與下一頁數據進行拼接
list: list.concat(this.data.list || []),
// 翻頁,頁碼+1
count: ++this.data.count
});
}
方案優勢:
1、自定義信息流組件和加載更多組件,可針對具體需求靈活處理
2、無三方組件引入,減少包體積
【場景二】
1、場景特徵:頁面整體由一塊位置固定的區域(A) + 一塊滾動區域(B) + 上滑加載更多(C)組成。
2、場景展示:
3、實現方案
方案一:採用 smart-ui 中的 feed 信息流組件 + spin 加載狀態組件
用百度開發者工具打開
關鍵代碼:
1)爲 feed 組件添加滾動區域的高度
<view class="page-container" >
<!-- 固定區域 -->
<view class="placeholder"></view>
<!-- feed 組件 -->
<smt-feed
<!-- 定義 pull-down-refresh 類,用於獲取組件實例-->
class="smt-feed pull-down-refresh"
<!-- 下拉刷新關鍵字 -->
pull-to-refresh
<!-- 手動觸發下拉刷新操作-->
bind:refresh="onRefresh"
<!-- 監聽頁面滾動到底部 -->
bind:scrolltolower="scrollToLower"
text="{{text}}"
>
<!-- 自定義信息流組件 -->
<list list="{{list}}"></list>
<!-- 加載狀態組件 -->
<smt-spin status="{{status}}" bind:tap="reload"></smt-spin>
</smt-feed>
</view>
/* 固定區域樣式 */
.placeholder {
height: 1.52rem;
background-color: #e0e0e0;
}
/* feed 組件樣式*/
.smt-feed {
display: block;
/* 滾動區域高度需要 - 固定區域高度(1.52rem)*/
height: calc(100vh - 1.52rem);
background: #fff;
}
2)初始化頁面數據
data: {
// 初始化加載狀態組件,默認展示“加載中”
status: 1,
// 初始化信息流組件數據
list: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
// 初始化頁面數據,默認頁碼爲0
count: 0
}
3)實現 feed下拉刷新效果,如需要實現自動或手動執行下拉刷新效果,需在組件上添加 pull-to-refresh 關鍵字
<view class="page-container" >
<!-- 固定區域 -->
<view class="placeholder"></view>
<!-- feed 組件 -->
<smt-feed
<!-- 定義 pull-down-refresh 類,用於獲取組件實例-->
class="smt-feed pull-down-refresh"
<!-- 下拉刷新關鍵字 -->
pull-to-refresh
<!-- 手動觸發下拉刷新操作-->
bind:refresh="onRefresh"
<!-- 監聽頁面滾動到底部 -->
bind:scrolltolower="scrollToLower"
text="{{text}}"
>
<!-- 自定義信息流組件 -->
<list list="{{list}}"></list>
<!-- 加載狀態組件 -->
<smt-spin status="{{status}}" bind:tap="reload"></smt-spin>
</smt-feed>
</view>
4)進入頁面時自動刷新
onShow() {
this.onRefresh();
}
5)手動下拉刷新
/**
* 下拉更新最新內容
*/
async onRefresh() {
// 1、獲取組件實例
const refresh = await this.selectComponent('.pull-down-refresh');
// 2、調用組件上的方法模擬下拉刷新
refresh.startRefresh();
// 3、獲取接口數據並更新頁面內容
const {data: list} = await this.fetchData();
await syncSetData(this, {
status: 1,
count: 0,
list: list || this.data.list,
text: list ? `爲你推薦${list.length}條更新` : '暫時沒有更新,休息一下'
});
// 4、關閉刷新
refresh.stopRefresh();
}
6)實現上滑加載更多內容
/**
* 上拉觸底加載更多
*/
async scrollToLower() {
const {data: list} = await this.fetchData();
const fail = this.data.count === 3;
const end = this.data.count === 5;
if (fail || end) {
this.setData({
status: fail ? 3 : 2
});
return;
}
await syncSetData(this, {
list: list.concat(this.data.list || []),
count: ++this.data.count
});
}
方案優勢:
1、使用feed組件,無需使用view自定義信息流組件,輕鬆實現下拉刷新頁面,頁面局部刷新等功能。
2、使用spin組件,無需使用view 自定義加載狀態組件,快速實現上滑加載更多功能。
方案二:採用 scroll-view 自定義信息流組件 + 自定義上滑加載更多組件
用百度開發者工具打開
關鍵代碼:
1)使用 scroll-vew 包裹滾動區域
<view class="page-container">
<!-- 固定區域 -->
<view class="placeholder"></view>
<!-- feed 組件 -->
<view class="feed-wrap">
<!-- scroll-view 組件 -->
<scroll-view class="scroll-wrap" scroll-y scroll-top="{= scrollTop =}" bindscrolltolower="fetchMoreData">
<list list="{{list}}"></list>
<loading-more status="{{status}}" bindtap="reload"></loading-more>
</scroll-view>
</view>
</view>
2)scroll-view 外層的 dom 的高度需要用整個頁面的高度減去固定區域的高度
/* 整個頁面高度爲100vh */
.page-container {
height: 100vh;
}
/* 固定區域高度 */
.placeholder {
height: 1.52rem;
background-color: #e0e0e0;
}
/* scroll-view 外層高度需要整個頁面高度 - 固定區域高度 */
.feed-wrap {
position: relative;
height: calc(100% - 1.52rem);
overflow: hidden;
}
/* scroll-view 高度 */
.scroll-wrap {
height: 100%;
}
3)通過scroll-view 上綁定的bindscrolltolower 方法,展示加載更多,並獲取下一頁數據,具體方法可參考【方案一-> 關鍵代碼6】
4)自定義loading-more 組件,實現下拉加載更多效果
// 傳入的 status 爲對應下標,組件展示對應文案
textConfig: {
type: Array,
value: ['點擊加載更多', '正在加載...', '已經到底啦', '加載失敗 點擊重新加載']
}
方案優勢:
1、scroll-view組件是小程序原生組件,方式靈活,使用於頁面任意滾動區域。
2、自定義 loading-more 組件,可針對具體需求實現組件樣式,同時可減少三方組件的引入,減少代碼體積。
【場景三】
1、場景特徵:整個頁面由 tabs 組件 (A) + swiper 組件(B)+ 自定義信息流組件(C) + 自定義加載更多組件(D)
2、場景展示:
3、實現方案:
方案一:採用 tabs 組件 + scroll-view 自定義信息流組件+ swiper 組件
用百度開發者工具打開
關鍵代碼:
1)頁面結構
<view class="page-container">
<!-- tabs 多標籤組件 -->
<tabs
class="tabs-wrap"
active-name="{{activeTabName}}"
class="border-bottom"
tabs-background-color="#fff"
tabs-underline-color="#000"
tabs-inactive-text-color="#666"
tabs-active-text-color="#000"
bindtabchange="handleTabChange"
>
<tab-item
s-for="tab in tabs"
name="{{tab.name}}"
label="{{tab.label}}"
/>
</tabs>
<view class="feed-container">
<!-- swiper 滑塊組件,可左右滑動展示區域 -->
<swiper
class="swiper"
current="{{activeSwiperId}}"
bindchange="handleSwiperChange">
<!-- swiper-item 滑塊內部組件,包裹了內部滾動區域 -->
<swiper-item
item-id="{{activeSwiperId}}"
s-for="data in swiperData">
<!-- 內部可滾動區域 -->
<scroll-view
class="swiper-scroll-view"
scroll-y
bindscrolltolower="fetchMorePageData">
<list list="{{data}}"></list>
<view class="loading-more-wrap">
<loading-more status="{{status}}" bindtap="reload" secureBottom="false"></loading-more>
</view>
</scroll-view>
</swiper-item>
</swiper>
</view>
</view>
2)配置 swiper以及scroll-view 的高度
/* 頁面整個高度 */
.page-container {
height: 100vh;
}
/* swiper 外部高度爲整個頁面高度 - tabs 組件高度 */
.feed-container {
width: 100%;
height: calc(100vh - 72.46rpx);
}
/* swiper 以及內部scroll-view 高度*/
.swiper,
.swiper-scroll-view {
height: 100%;
}
- 通過 scroll-view 上綁定的 bindscrolltolower 方法,展示加載更多,並獲取下一頁數據。具體方式可參考【方案一->關鍵代碼6】。
方案優勢:
1、使用 swiper + scroll-view 實現信息流在 swiper-item 中滾動,無需動態計算信息流高度。
2、切換 tab 時,能保留每個 tab 下用戶上次瀏覽的位置。
方案二:採用 tabs 組件 + view 自定義信息流組件 + swiper 組件
用百度開發者工具打開
關鍵代碼:
1)頁面結構
<!-- swiper 高度 swiperH 需要動態計算 -->
<swiper class="swiper" style="height: {{swiperH}}" current="{{currentTab}}" bindchange="swiperChange">
<swiper-item class="item">
<view class="goodsList">
<view s-for="item,index in goods">
<view class="goodsItem">
<view class="goodsImage">
<image bindload="imageLoad" src="{{item.img}}"></image>
</view>
<view class="goodsTitle">
<text>{{item.title}}</text>
</view>
</view>
</view>
</view>
<view class="loading">努力加載中...</view>
</swiper-item>
</swiper>
2)動態計算 swiper 高度
// swiper-item 上的 class
swan.createSelectorQuery().selectAll('.item')
.boundingClientRect(rect => {
this.setData({
swiperH: rect[currentTab].height + 'px'
});
}).exec();
3)swiper 以及 swiper-item 樣式配置
.swiper{
min-height: 100vh;
width: 100%;
}
.swiper swiper-item {
/* ------- 需要注意這裏 ------- */
height: auto !important;
overflow: auto;
}
4)監聽頁面滑動到底部時,觸發 onReachBottom,執行加載更多操作
onReachBottom() {
// 由於測試接口返回數據過快,爲了方便測試所以延遲一分鐘發送請求
setTimeout(() => {
this.initData();
}, 1000);
}
【場景四】
1、場景特徵:一塊固定區域(A) + tabs 多標籤(B) + 展示區(C),其中展示區域(C)與 tabs 一起滾動,當滑動到頁面頂端時 tabs 吸頂,展示區域(C)內部滾動
2、場景展示:
3、實現方案:
方案一:採用 tabs 組件 + swiper 組件 + scroll-view 自定義信息流組件
用百度開發者工具打開
關鍵代碼:
1)tabs 組件定位爲 sticky,swiper 高度爲 100vh
.tabs-wrap {
height: 50px;
width: 100%;
position: sticky;
top: 0;
z-index: 100;
}
.swiper {
height: 100vh;
}
2)通過動態配置 scroll-view 的 scroll-y 屬性,實現信息流在 swiper-item 中滾動
<scroll-view
<!-- 信息流初始狀態爲不滾動 -->
scroll-y="{{enableScroll}}"
bindscrolltolower="fetchMorePageData"
bindscroll="onAppsScroll"
style="height: 100%">
<list list="{{list}}"></list>
<loading-more status="{{status}}" bindtap="handleSpin"></loading-more>
</scroll-view>
3)頁面獲取 tabs 數據後,計算 tabs 組件的位置
/**
* 計算tabs的高度
*/
calTabsFixedTop() {
// tabs-wrap 爲tabs 組件上綁定的class
swan.createSelectorQuery()
.select('.tabs-wrap')
.boundingClientRect(res => {
if (!res) {
return;
}
this.setData({
tabFixedTop: res.top
});
})
.exec();
}
4)默認頁面監聽頁面滾動,當 tabs 組件滾動到頂部時,配置內容區域開始滾動: scroll-y 爲 true
/**
* 監聽頁面滾動
*
* @param {Object} event 滾動事件對象
*/
onPageScroll(event) {
const {fixedTabs, tabFixedTop} = this.data;
const currentFixedTabs = event.scrollTop >= tabFixedTop;
// 避免重複setData
if (currentFixedTabs !== fixedTabs) {
this.setData({
// 當頁面滾動的高度大於tabs原來的高度時,說明tabs已經到頁面頂部,需要呈現吸頂態
enableScroll: currentFixedTabs
});
}
}
總結
在上述的四種場景中,前兩個場景功能較爲簡單,基本通過 view + onReachBottom 或者 scroll-view + bindscrolltolower 配合實現,後兩個場景均屬於多 tab + swiper 場景,功能包含了 tab 切換以及內容區的左右橫滑等,功能較爲複雜,但也是較爲常用的信息流配合其他功能的組合打法,下面給出不同場景下,具體方案的優勢和劣勢,大家可以有針對性的選擇不同的實現方案。
場景 | 方案 | 優勢 | 劣勢 |
---|---|---|---|
場景一 (頁面整體由一塊滾動區域 + 上滑加載更多組成) | page-feed 信息流模版 | 拿來即用,快速實現信息流分頁加載 | 場景單一,整個頁面僅包含信息流和上滑加載更多 |
view 自定義信息流組件 + onReachBottom | 方式靈活,可針對具體需求靈活處理 | 信息流功能均需要自己開發,成本較高 | |
場景二 (頁面整體由一塊位置固定的區域 + 一塊滾動區域 + 上滑加載更多組成) | smart-ui 中的 feed 信息流組件 + 加載狀態組件 | 無需自定義信息流組件以及加載狀態組件 | feed 組件包含了 scroll-view 組件,默認會出現區域滾動區域,不適用【信息流跟隨整個頁面一起滾動】此類需求 |
scroll-view 自定義信息流組件 + 自定義上滑加載更多組件 | 方式靈活,可針對具體需求靈活處理,可配置 scroll-view 區域是否滾動 | scroll-view 組件使用,需要參照官方文檔,按照組件要求開發 | |
場景三 (整個頁面由 tabs + swiper + 自定義信息流組件 + 自定義加載更多組件組成) | tabs 組件 + scroll-view 自定義信息流組件+ swiper 組件 | 無需動態計算信息流高度,能保留每個 tab 下用戶上次瀏覽的位置 | 1、使用到 swiper 組件多層級嵌套,樣式需要準確書寫<br>2、使用 swiper 需要一次性獲取所有 tabs 下的數據,並且對數據格式有一定的要求(二維數組) |
tabs 組件 + view 自定義信息流組件 + swiper 組件 | 方式靈活,可針對具體需求靈活處理 | 每次請求下一頁數據時,需要根據當前列表數據長度,動態計算 swiper 高度 | |
場景四 (頁面整個由一塊固定區域 + tabs 多標籤 + 一塊展示區組成且tabs滾動到頁面頂部時吸頂,展示區開始滾動) | tabs 組件 + swiper 組件 + scroll-view 自定義信息流組件 | 方式靈活,可適用大多數信息流分頁展示場景 | 需要監聽頁面滾動,動態計算 tabs 位置,來設置 scroll-view 是否滾動 |