快速实现上滑加载更多
实现方式
在智能小程序的开发过程中,经常会遇到页面列表数量较多的情况,此时可以通过【分页】加载数据,并监听页面滑动到底部时触发【上滑加载更多】,从而增加页面首屏渲染速度。 想要实现这种分页展示数据,上滑加载更多的效果,主要有以下几种方式:
- 使用 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 | 页面加载到底部时触发 | 页面事件函数 |
针对不同的场景,可以使用不同的方式实现,下面我从四个常见的场景进行具体分析。
分场景描述
【场景一】
- 场景特征:页面整体由一块滚动区域(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)配置页面高度为整个视口高度
<!-- 需要配置页面高度为整个视口高度 --> <view class="page-container" style="height: 100vh"> <!-- 自定义信息流组件 --> <list list="{{list}}"></list> <!-- 自定义上滑加载更多组件 --> <loading-more status="{{status}}" bind:tap="reload"></loading-more> </view>
2)页面 onLoad 时获取首屏数据
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时触发加载更多内容
/** * 监听页面滚动到底部,触发加载更多 */ 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 }); }
方案优势:
1、自定义信息流组件和加载更多组件,可针对具体需求灵活处理
2、无三方组件引入,减少包体积
【场景二】
1、场景特征:页面整体由一块位置固定的区域(A) + 一块滚动区域(B) + 上滑加载更多(C)组成。
2、场景展示:
3、实现方案
方案一:采用 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() { // 1、获取组件实例 const refresh = await this.selectComponent('.pull-down-refresh'); // 2、调用组件上的方法模拟下拉刷新 refresh.startRefresh(); // 3、获取接口数据并更新页面内容 const {data: list} = await this.fetchData(); await syncSetData(this, { status: 1, count: 0, list: list || this.data.list, text: list ? `为你推荐${list.length}条更新` : '暂时没有更新,休息一下' }); // 4、关闭刷新 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 }); }
方案优势:
1、使用feed组件,无需使用view自定义信息流组件,轻松实现下拉刷新页面,页面局部刷新等功能。
2、使用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: ['点击加载更多', '正在加载...', '已经到底啦', '加载失败 点击重新加载'] }
方案优势:
1、scroll-view组件是小程序原生组件,方式灵活,使用于页面任意滚动区域。
2、自定义 loading-more 组件,可针对具体需求实现组件样式,同时可减少三方组件的引入,减少代码体积。
【场景三】
1、场景特征:整个页面由 tabs 组件 (A) + swiper 组件(B)+ 自定义信息流组件(C) + 自定义加载更多组件(D)
2、场景展示:
3、实现方案:
方案一:采用 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】。
方案优势:
1、使用 swiper + scroll-view 实现信息流在 swiper-item 中滚动,无需动态计算信息流高度。
2、切换 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); }
【场景四】
1、场景特征:一块固定区域(A) + tabs 多标签(B) + 展示区(C),其中展示区域(C)与 tabs 一起滚动,当滑动到页面顶端时 tabs 吸顶,展示区域(C)内部滚动
2、场景展示:
3、实现方案:
方案一:采用 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 下用户上次浏览的位置 | 1、使用到 swiper 组件多层级嵌套,样式需要准确书写<br>2、使用 swiper 需要一次性获取所有 tabs 下的数据,并且对数据格式有一定的要求(二维数组) |
tabs 组件 + view 自定义信息流组件 + swiper 组件 | 方式灵活,可针对具体需求灵活处理 | 每次请求下一页数据时,需要根据当前列表数据长度,动态计算 swiper 高度 | |
场景四 (页面整个由一块固定区域 + tabs 多标签 + 一块展示区组成且tabs滚动到页面顶部时吸顶,展示区开始滚动) | tabs 组件 + swiper 组件 + scroll-view 自定义信息流组件 | 方式灵活,可适用大多数信息流分页展示场景 | 需要监听页面滚动,动态计算 tabs 位置,来设置 scroll-view 是否滚动 |

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
快手基于 Apache Flink 的优化实践
本次由快手刘建刚老师分享,内容主要分为三部分。首先介绍流式计算的基本概念, 然后介绍 Flink 的关键技术,最后讲讲 Flink 在快手生产实践中的一些应用,包括实时指标计算和快速 failover。 一、流式计算的介绍 流式计算主要针对 unbounded data(无界数据流)进行实时的计算,将计算结果快速的输出或者修正。 这部分将分为三个小节来介绍。第一,介绍大数据系统发展史,包括初始的批处理到现在比较成熟的流计算;第二,为大家简单对比下批处理和流处理的区别;第三,介绍流式计算里面的关键问题,这是每个优秀的流式计算引擎所必须面临的问题。 1、大数据系统发展史 上图是 2003 年到 2018 年大数据系统的发展史,看看是怎么一步步走到流式计算的。 2003 年,Google 的 MapReduce 横空出世,通过经典的 Map&Reduce 定义和系统容错等保障来方便处理各种大数据。很快就到了 Hadoop,被认为是开源版的 MapReduce, 带动了整个apache开源社区的繁荣。再往后是谷歌的 Flume,通过算子连接等 pipeline 的方式解决了多个 Map...
- 下一篇
解密JVM虚拟机底层原理【 本地方法栈】
前言 文章奔着简洁易懂的形式去写,不会有很多花哨的废话,尽可能简明扼要的描述清楚想要表达的一些东西,如果你想深入了解JVM底层,不妨花几分钟仔细看看,本节说说本地方法栈 之前说过了 程序计数器 虚拟机栈 特点以及作用 今天接着说 本地方法栈 本节简单易懂,仔细观看 声明: 我们再说Java内存结构时,一直在说,栈、堆、方法区 但是殊不知 栈又分为 虚拟机栈 和 本地方法栈 先来看看本地方法栈的图片 什么意思呢? 就是Java虚拟机在调用本地方法时,需要给本地方法提供的一块空间 什么又是本地方法呢? 就是那些 不是由Java代码编写的方法 科普,因为Java代码有限制,有些情况下不能和操作系统底层进行交互,所以就需要由C 或 C++ 编写的本地方法来与操作系统打交道 所以本地方法执行时候所使用的就是本地方法栈 举例 这样的本地方法其实是很多的,但有些同学说我没见过这些本地方法啊?或者说什么样的才是称为本地方法呢? 带大家看下 object 是所有Java的父类 可以看到object的clone() 克隆方法就是做一个对象的克隆 可以看到修饰符是native 可以看到native修饰的方法...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2整合Redis,开启缓存,提高访问速度
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- Red5直播服务器,属于Java语言的直播服务器
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- Windows10,CentOS7,CentOS8安装Nodejs环境
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- 2048小游戏-低调大师作品
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- SpringBoot2整合Thymeleaf,官方推荐html解决方案