實現方式
在智能小程序的開發過程當中,常常會遇到頁面列表數量較多的狀況,此時能夠經過【分頁】加載數據,並監聽頁面滑動到底部時觸發【上滑加載更多】,從而增長頁面首屏渲染速度。 想要實現這種分頁展現數據,上滑加載更多的效果,主要有如下幾種方式:npm
- 使用 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 | 頁面加載到底部時觸發 | 頁面事件函數 |
針對不一樣的場景,可使用不一樣的方式實現,下面我從四個常見的場景進行具體分析。api
分場景描述
【場景一】
- 場景特徵:頁面總體由一塊滾動區域(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)配置頁面高度爲整個視口高度app
<!-- 須要配置頁面高度爲整個視口高度 --> <view class="page-container" style="height: 100vh"> <!-- 自定義信息流組件 --> <list list="{{list}}"></list> <!-- 自定義上滑加載更多組件 --> <loading-more status="{{status}}" bind:tap="reload"></loading-more> </view>
2)頁面 onLoad 時獲取首屏數據dom
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時觸發加載更多內容async
/** * 監聽頁面滾動到底部,觸發加載更多 */ 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 }); }
方案優點:
一、自定義信息流組件和加載更多組件,可針對具體需求靈活處理
二、無三方組件引入,減小包體積ide
【場景二】
一、場景特徵:頁面總體由一塊位置固定的區域(A) + 一塊滾動區域(B) + 上滑加載更多(C)組成。
二、場景展現:
三、實現方案函數
方案一:採用 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() { // 一、獲取組件實例 const refresh = await this.selectComponent('.pull-down-refresh'); // 二、調用組件上的方法模擬下拉刷新 refresh.startRefresh(); // 三、獲取接口數據並更新頁面內容 const {data: list} = await this.fetchData(); await syncSetData(this, { status: 1, count: 0, list: list || this.data.list, text: list ? `爲你推薦${list.length}條更新` : '暫時沒有更新,休息一下' }); // 四、關閉刷新 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 }); }
方案優點:
一、使用feed組件,無需使用view自定義信息流組件,輕鬆實現下拉刷新頁面,頁面局部刷新等功能。
二、使用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: ['點擊加載更多', '正在加載...', '已經到底啦', '加載失敗 點擊從新加載'] }
方案優點:
一、scroll-view組件是小程序原生組件,方式靈活,使用於頁面任意滾動區域。
二、自定義 loading-more 組件,可針對具體需求實現組件樣式,同時可減小三方組件的引入,減小代碼體積。
【場景三】
一、場景特徵:整個頁面由 tabs 組件 (A) + swiper 組件(B)+ 自定義信息流組件(C) + 自定義加載更多組件(D)
二、場景展現:
三、實現方案:
方案一:採用 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】。
方案優點:
一、使用 swiper + scroll-view 實現信息流在 swiper-item 中滾動,無需動態計算信息流高度。
二、切換 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); }
【場景四】
一、場景特徵:一塊固定區域(A) + tabs 多標籤(B) + 展現區(C),其中展現區域(C)與 tabs 一塊兒滾動,當滑動到頁面頂端時 tabs 吸頂,展現區域(C)內部滾動
二、場景展現:
三、實現方案:
方案一:採用 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 下用戶上次瀏覽的位置 | 一、使用到 swiper 組件多層級嵌套,樣式須要準確書寫<br>二、使用 swiper 須要一次性獲取全部 tabs 下的數據,而且對數據格式有必定的要求(二維數組) |
tabs 組件 + view 自定義信息流組件 + swiper 組件 | 方式靈活,可針對具體需求靈活處理 | 每次請求下一頁數據時,須要根據當前列表數據長度,動態計算 swiper 高度 | |
場景四 (頁面整個由一塊固定區域 + tabs 多標籤 + 一塊展現區組成且tabs滾動到頁面頂部時吸頂,展現區開始滾動) | tabs 組件 + swiper 組件 + scroll-view 自定義信息流組件 | 方式靈活,可適用大多數信息流分頁展現場景 | 須要監聽頁面滾動,動態計算 tabs 位置,來設置 scroll-view 是否滾動 |