項目中遇到 tab切換列表,每一個tab都須要分頁的需求,分頁流程具備類似性,因而想將分頁封裝爲組件,方便應用。javascript
組件的應用已寫成一個小demo,效果以下圖所示(數據用mock模擬):html
源碼能夠查看:wxapp-pagination前端
具體項目需求:java
固然,做爲前端,要考慮性能方面的需求:git
因此原型圖大概就長這樣:
github
與分頁邏輯相關的項目結構以下:json
├── components
│ ├── meeting-item # 列表item
│ └── pagination # 分頁組件
├── model
│ └── user.js # 個人相關 model
└── pages
│ └── user # 個人相關頁面
│ ├── meetings # 個人會議(就是tab要分頁的頁面啦)
│ └── ...
│
└── vant-weapp
複製代碼
仍是用圖理一下他們之間的關係吧:小程序
觸發分頁的事件是滾動到頁面的底部,小程序中,觸發該事件是Page頁面的onReachBottom
事件,可是這個事件只能在Page中觸發,因此要將這個觸發時機傳遞給pagination組件。api
解決思路是:組件 pagination 內,設置key
屬性,每當onReachBottom
事件觸發以後,設置組件屬性 key
爲一個隨機字符串,當組件 pagination 監聽到key
的變化的時候,作出分頁操做。bash
// components/pagination/index.js
Component({
properties: {
key: {
type: String,
observer: '_loadMoreData' // _loadMoreData 爲分頁操做
}
}
})
複製代碼
<!-- pages/user/meetings/index.wxml -->
<tabs active="{{currentTab}}" bind:change="onChange">
<tab title="個人會議" data-key="{{type['JOIN']}}">
<view class="meeting-list">
<pagination name="JOIN" key="{{joinKey}}" >
</pagination>
</view>
</tab>
<tab title="個人預定">
<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)
})
}
})
複製代碼
觸發到達底部以後,須要加載數據。但再加載以前,先知足加載的條件:
具體加載流程爲:
key
值key
值監聽到變化,觸發加載事件_loadMoreData
_loadMoreData
中判斷知足條件後,觸發加載列表函數 this.triggerEvent('getList')
,並傳入頁面參數page 和 size。list
和total
值。list
監聽到變化,判斷是否加載完成。// components/pagination/index.js
Component({
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
})
}
}
})
複製代碼
<!-- pages/user/meetings/index.wxml -->
<pagination name="BOOK" key="{{bookKey}}" bind:getList="getBookMeetings" list="{{bookMeetings}}" total="{{bookTotal}}" >
</pagination>
複製代碼
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.js
Component({
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。