項目中遇到 tab切換列表,每個tab都需要分頁的需求,分頁流程具有相似性,于是想將分頁封裝為組件,方便應用。
組件的應用已寫成一個小demo,效果如下圖所示(數據用mock模擬):
源碼可以查看:wxapp-pagination
項目需求
具體項目需求:
當然,作為前端,要考慮性能方面的需求:
所以原型圖大概就長這樣:
邏輯實現
與分頁邏輯相關的項目結構如下:
├── components│ ├── meeting-item # 列表item│ └── pagination # 分頁組件├── model│ └── user.js # 我的相關 model└── pages│ └── user # 我的相關頁面│ ├── meetings # 我的會議(就是tab要分頁的頁面啦)│ └── ...│└── vant-weapp
還是用圖理一下他們之間的關系吧:
在組件內監聽觸發分頁事件
觸發分頁的事件是滾動到頁面的底部,小程序中,觸發該事件是Page頁面的onReachBottom事件,但是這個事件只能在Page中觸發,所以要將這個觸發時機傳遞給pagination組件。
解決思路是:組件 pagination 內,設置key屬性,每當onReachBottom事件觸發之后,設置組件屬性 key 為一個隨機字符串,當組件 pagination 監聽到key的變化的時候,做出分頁操作。
// components/pagination/index.jsComponent({ properties: { key: { type: String, observer: '_loadMoreData' // _loadMoreData 為分頁操作 } }})
<!-- pages/user/meetings/index.wxml --><tabs active="{{currentTab}}" bind:change="onChange"> <tab <view class="meeting-list"> <pagination name="JOIN" key="{{joinKey}}" > </pagination> </view> </tab> <tab <view class="meeting-list"> <pagination name="BOOK" key="{{bookKey}}" > </pagination> </view> </tab></tabs>
Page({ onReachBottom(){ const key = scene[+this.data.currentTab].key // 對應tab對應key this.setData({ [key]: random(16) }) }})
分頁組件的實現邏輯
觸發到達底部之后,需要加載數據。但再加載之前,先滿足加載的條件:
具體加載流程為:
component
// components/pagination/index.jsComponent({ properties: { name: String, key: { type: String, observer: '_loadMoreData' // _loadMoreData 為分頁操作 }, size: { // 每次加載條目數 type: Number, value: 10 }, total: Number, // 頁面總數 list: { // 已加載條目 type: Array, observer: '_endState' // 每次加載完新數據,判斷數據是否全部加載完成 } }, data: { page: 0, // 當前第幾頁 loading: false, // 是否正在加載 ended: false // 數據是否已全部加載完成 }, methods: { _loadMoreData(){ const { loading, ended, size } = this.data if (loading) return // 上一頁還未加載完成,不加載 if (ended) return // 當前頁面全部加載完,不加載 const page = this.data.page + 1 this.setData({ loading: true, // 開始加載新頁面loading設置為true page }) // 觸發加載下一頁,并傳入參數 this.triggerEvent('getList', { page, size }) }, _endState(val, oldVal) { const { total, list } = this.properties let ended = false // 判斷數據是否全部加載完成 if (list.length >= total) { ended = true } this.setData({ loading: false, // 當前頁面加載完成,loading設置為false ended }) } }})
page
<!-- pages/user/meetings/index.wxml --><pagination name="BOOK" key="{{bookKey}}" bind:getList="getBookMeetings" list="{{bookMeetings}}" total="{{bookTotal}}"></pagination>
循環列表item
pagination組件獲取了可循環的列表,初始想法是循環slot,但是在slot中卻獲取不到 item 對象:
<view wx:for="{{list}}" wx:for-item="item" wx:key="index"> <slot></slot></view>
解決的辦法是將每個列表項封裝為組件,循環抽象節點,這樣對其他頁面的分頁具有可拓展性。
componentGenerics 字段中聲明:
// components/pagination/index.json{ "componentGenerics": { "selectable": true }, // ...}
使用抽象節點:
<!-- components/pagination/index.wxml --><view wx:for="{{list}}" wx:for-item="item" wx:key="index"> <selectable item="{{item}}"></selectable></view>
指定“selectable”具體是哪個組件:
<!-- pages/user/meetings/index.wxml --><pagination generic:selectable="meeting-item" // ... 其他屬性></pagination>
對應 json 文件定義對應 usingComponents :
// pages/user/meetings/index.json{ "usingComponents": { "pagination":"/components/pagination/index", "meeting-item":"/components/meeting-item/index" }}
meeting-item 組件接收一個屬性 item,這樣在 meeting-item 組件中,就可以獲取到循環列表的item值,并正常繪制頁面。
實現切換懶加載
給pagination添加initImmediately屬性,當initImmediately為true時,首次加載頁面,并用變量 initState標記是否已經初始化頁面過。
// components/pagination/index.jsComponent({ properties: { initImmediately: { type: Boolean, value: true, observer: function(val){ if (val && !this.data.initState) { this.initData() } } }, // ... }, data: { initState: false, // 是否已經加載過 // ... }, lifetimes: { attached: function () { if (this.data.initImmediately){ this.initData() } }, }, methods: { initData(){ console.info(`${this.data.name}:start init data`) this._loadMoreData() this.setData({ initState: true }) }, // ... _endState(val, oldVal) { if (!this.data.initState) return // ... }, }})
當currentTab為當前類型的時候,initImmediately 設置為 true。
<!-- pages/user/meetings/index.wxml --><pagination name="JOIN" init-immediately="{{currentTab==type['JOIN']}}" // ...></pagination><pagination name="BOOK" init-immediately="{{currentTab==type['BOOK']}}" // ...></pagination>
組件的復用
完成了以上組件,在對其他分頁的頁面,可以直接復用。比如,實現一個獲取全部用戶列表的分頁,只需要新增一個user-item的組件,像這樣調用:
<pagination name="USER" key="{{key}}" bind:getList="getUserList" list="{{userList}}" total="{{userTotal}}" generic:selectable="user-item"></pagination>
具體應用可以查看我寫的小demo:wxapp-pagination。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VEVB武林網。
新聞熱點
疑難解答