项目中遇到 tab切换列表,每个tab都需要分页的需求,分页流程具有相似性,于是想将分页封装为组件,方便应用。
组件的应用已写成一个小demo,效果如下图所示(数据用mock模拟):
源码可以查看:wxapp-pagination
项目需求
具体项目需求:
- 查看自己相关的会议(页面命名为 meetings)
- tab切换,分为:
- “我的会议”(我参加的会议,后面会以 "join" 为 key区分)
- “我的预约”(我预约的会议,后面会以 "book" 为 key区分)
- 一次加载10条(size=10),拉到底部后,加载下一页(page = page +1)
当然,作为前端,要考虑性能方面的需求:
- 首次只加载默认tab页的首页,其他tab等到点击到对应tab才开始加载。
- 回到已加载过的tab页,保留原数据不重新加载。
所以原型图大概就长这样:
逻辑实现
与分页逻辑相关的项目结构如下:
├── 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.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)
})
}
})
分页组件的实现逻辑
触发到达底部之后,需要加载数据。但再加载之前,先满足加载的条件:
- 上一页还未加载完成(loading = true),不重复加载
- 当前页面全部加载完(ended = true),不继续加载
具体加载流程为:
- page:触发 onReachBottom 事件,修改 pagination组件 key 值
- component: key值监听到变化,触发加载事件 _loadMoreData
- component: _loadMoreData 中判断满足条件后,触发加载列表函数 this.triggerEvent('getList'),并传入页面参数page 和 size。
- page:向model层获取数据。
- page:获取数据后,传递给 pagination组件list和total 值。
- component:list 监听到变化,判断是否加载完成。
component
// 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
})
}
}
})
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.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。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
更新日志
- 小骆驼-《草原狼2(蓝光CD)》[原抓WAV+CUE]
- 群星《欢迎来到我身边 电影原声专辑》[320K/MP3][105.02MB]
- 群星《欢迎来到我身边 电影原声专辑》[FLAC/分轨][480.9MB]
- 雷婷《梦里蓝天HQⅡ》 2023头版限量编号低速原抓[WAV+CUE][463M]
- 群星《2024好听新歌42》AI调整音效【WAV分轨】
- 王思雨-《思念陪着鸿雁飞》WAV
- 王思雨《喜马拉雅HQ》头版限量编号[WAV+CUE]
- 李健《无时无刻》[WAV+CUE][590M]
- 陈奕迅《酝酿》[WAV分轨][502M]
- 卓依婷《化蝶》2CD[WAV+CUE][1.1G]
- 群星《吉他王(黑胶CD)》[WAV+CUE]
- 齐秦《穿乐(穿越)》[WAV+CUE]
- 发烧珍品《数位CD音响测试-动向效果(九)》【WAV+CUE】
- 邝美云《邝美云精装歌集》[DSF][1.6G]
- 吕方《爱一回伤一回》[WAV+CUE][454M]


