项目中遇到 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。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
小程序,分页,小程序,可复用分页
RTX 5090要首发 性能要翻倍!三星展示GDDR7显存
三星在GTC上展示了专为下一代游戏GPU设计的GDDR7内存。
首次推出的GDDR7内存模块密度为16GB,每个模块容量为2GB。其速度预设为32 Gbps(PAM3),但也可以降至28 Gbps,以提高产量和初始阶段的整体性能和成本效益。
据三星表示,GDDR7内存的能效将提高20%,同时工作电压仅为1.1V,低于标准的1.2V。通过采用更新的封装材料和优化的电路设计,使得在高速运行时的发热量降低,GDDR7的热阻比GDDR6降低了70%。
更新动态
- 雨林唱片《赏》新曲+精选集SACD版[ISO][2.3G]
- 罗大佑与OK男女合唱团.1995-再会吧!素兰【音乐工厂】【WAV+CUE】
- 草蜢.1993-宝贝对不起(国)【宝丽金】【WAV+CUE】
- 杨培安.2009-抒·情(EP)【擎天娱乐】【WAV+CUE】
- 周慧敏《EndlessDream》[WAV+CUE]
- 彭芳《纯色角3》2007[WAV+CUE]
- 江志丰2008-今生为你[豪记][WAV+CUE]
- 罗大佑1994《恋曲2000》音乐工厂[WAV+CUE][1G]
- 群星《一首歌一个故事》赵英俊某些作品重唱企划[FLAC分轨][1G]
- 群星《网易云英文歌曲播放量TOP100》[MP3][1G]
- 方大同.2024-梦想家TheDreamer【赋音乐】【FLAC分轨】
- 李慧珍.2007-爱死了【华谊兄弟】【WAV+CUE】
- 王大文.2019-国际太空站【环球】【FLAC分轨】
- 群星《2022超好听的十倍音质网络歌曲(163)》U盘音乐[WAV分轨][1.1G]
- 童丽《啼笑姻缘》头版限量编号24K金碟[低速原抓WAV+CUE][1.1G]