乐于分享
好东西不私藏

Uniapp实现下拉刷新上拉加载更多功能开发实战教程

Uniapp实现下拉刷新上拉加载更多功能开发实战教程

书接上篇,我们今天继续来学习一下 UniApp 如何实现页面顶部下拉刷新功能和页面底部上拉加载更多数据功能,附带了完整的设计思路和源码示例,以及常见避坑指南和开发实践。

此系列文章将带领你从移动端跨平台开发入门到精通,如果你也喜欢关注APP、小程序、公众号、H5等等应用的开发,可以持续关注后续更新,避免错过宝贵的知识分享。

致开发者的忠告: AI编程盛行的今天,我们并不是不需要学习技术,而是更应该专研技术,拥有把控全局的架构设计思维才能在AI盛行的未来有立足之地。

言归正传,咱们今天继续来聊一个几乎每个项目都跑不掉的需求(让页面交互活过来):下拉刷新页面数据,上拉加载更多内容

你可能会想:“不就是加两个回调函数吗,有啥难的?”但很多新手写着写着就发现:下拉没反应、上拉不触发、scroll-view滚动不了、数据重复加载…… 然后开始怀疑人生。

别慌,今天咱就把这事儿彻底讲透。页面滚动scroll-view各有什么优缺点?什么场景该用哪个?常见坑怎么绕过去?我统统掰开揉碎了喂给你。


一、先分清两个概念:页面滚动 vs 区域滚动

  • 页面滚动:整个页面就是一个滚动容器,滚动条在<page>上。→ 对应 UniApp 的 页面级生命周期onPullDownRefreshonReachBottom

  • 区域滚动:页面里某个固定高度的<scroll-view>内部滚动。→ 对应 scroll-view 组件 的事件:@refresherrefresh(下拉)、@scrolltolower(上拉)

核心区别一句话页面滚动是全局的,scroll-view 是局部的。用哪个,取决于你的页面布局。


二、下拉刷新:两种姿势,任你挑选

🅰️ 方案一:页面级下拉刷新(全页生效)

适合场景:整个页面就是一个列表,没有复杂的自定义头部,或者头部固定在顶部不参与滚动。

步骤 1:在 pages.json 里开启

{"pages": [    {"path""pages/index/index","style": {"enablePullDownRefresh"true,"backgroundColor""#f5f5f5"// 下拉背景色      }    }  ]}

步骤 2:在页面 JS 里监听 onPullDownRefresh

<scriptsetup>import { onPullDownRefresh } from'@dcloudio/uni-app'onPullDownRefresh(() => {console.log('下拉刷新触发')// 重新请求第一页数据fetchData(1).then(() => {// 数据刷新完毕,手动关闭下拉刷新动画uni.stopPullDownRefresh()  })})</script>

✅ 优点:  

  • 零配置滚动容器,不用操心高度计算  

  • 下拉动画是系统级的,体验很顺滑  

❌ 缺点:  

  • 整个页面都在下拉刷新范围内,如果你的页面顶部有导航栏、tab标签且不希望被下拉,就尴尬了  

  • 关闭刷新必须手动调uni.stopPullDownRefresh(),容易忘  


🅱️ 方案二:scroll-view 下拉刷新(区域生效)

适合场景:页面有固定头部(比如自定义导航栏、tab切换),只有列表区域可下拉

代码示例

<template><view><!-- 固定的头部,不下拉 --><viewclass="header">我是头部</view><!-- 可滚动的列表区域 --><scroll-viewclass="scroll-list"scroll-yrefresher-enabled:refresher-triggered="refreshing"@refresherrefresh="onRefresh"><viewv-for="item in list":key="item.id">{{ item.title }}</view></scroll-view></view></template><scriptsetup>import { ref } from'vue'constrefreshing=ref(false)constlist=ref([])constonRefresh= () => {refreshing.value=truefetchData(1).then(() => {refreshing.value=false// 关闭下拉刷新状态  })}</script>

关键点:  

  • refresher-enabled:开启下拉刷新  

  • :refresher-triggered:控制刷新状态(true 显示加载动画,false 隐藏)  

  • @refresherrefresh:下拉刷新触发的事件  

✅ 优点:  

  • 只对指定的滚动区域生效,不会拖拽整个页面  

  • 状态完全可控(refresher-triggered)  

❌ 缺点:  

  • 必须给 scroll-view 设置固定高度(或使用 flex:1 + 父容器固定高度),否则无法滚动  

  • 下拉动画没有页面级那么丝滑(但可接受)  


三、上拉加载更多:同样两条路

🅰️ 页面级上拉加载(配合 onReachBottom

适用场景:与页面级下拉刷新配套,整个页面滚动到底部触发。

步骤 1:pages.json 里可配置距离

{"path""pages/index/index","style": {"onReachBottomDistance"50// 默认也是50,单位px  }}

步骤 2:页面 JS 监听

<scriptsetup>import { onReachBottom } from'@dcloudio/uni-app'onReachBottom(() => {console.log('触底了,加载下一页')loadMore()})</script>

✅ 优点:简单,无脑。❌ 缺点:  

  • 只要滚动到底部就触发,不管是不是真的有更多数据(需要你自己加锁)  

  • 如果页面底部有“已经到底了”提示,可能会在提示区域也被触发(需要自己控制距离)  


🅱️ scroll-view 上拉加载(@scrolltolower

适用场景:区域滚动时,滚动到底部触发。

<scroll-viewclass="scroll-list"scroll-y@scrolltolower="loadMore"lower-threshold="50"><viewv-for="item in list":key="item.id">{{ item.title }}</view><viewv-if="loading"class="loading">加载中...</view><viewv-if="finished"class="no-more">没有更多了</view></scroll-view>

lower-threshold:距离底部多少像素时触发(默认50)。

✅ 优点:  

  • 只针对这个滚动区域,不影响页面其他部分  

  • 可精细控制加载状态  

❌ 缺点:  

  • 同下拉刷新,必须设置固定高度

  • 如果列表数据不满一屏,无法滚动,scrolltolower 不会触发(这是符合预期的)  


四、核心对比:页面滚动 vs scroll-view

对比维度 页面级滚动 (onPullDownRefresh / onReachBottom) scroll-view 滚动
适用布局 整页滚动,无固定头部/底部 固定头部/底部,列表区域独立滚动
高度设置 无需设置,自动占满 必须设置高度,否则不滚动
下拉刷新 需 pages.json 配置,自动动画,需手动停止 需 refresher-enabled,状态双向绑定
上拉加载 onReachBottom 自动监听 @scrolltolower 监听
性能 较好(原生滚动) 依赖渲染层,大数据量时稍逊
灵活性 低,全页面行为 高,可随意放置,可嵌套多个
H5 兼容 完美 完美
小程序兼容 完美 完美

一句话选型口诀:  

  • 页面清爽无固定头 → 页面级滚动(简单省事)  

  • 头部固定 tab 切换 → scroll-view(区域滚动)  

  • 既要下拉又要上拉 → 建议统一用一种,别混用  


五、不触发?不滚动?常见坑及填坑实录

💥 坑1:页面级下拉刷新死活不触发

现象:手指疯狂下拉,动画就是不出现。可能原因:  

  1. pages.json 里没有设置 "enablePullDownRefresh": true

  2. 页面是 scroll-view 滚动,页面本身没有滚动 → 页面级下拉刷新只在页面滚动时生效,如果页面被 scroll-view 撑满,页面本身不滚动,下拉刷新不会触发!  

  3. 小程序基础库版本过低(极少见)

✅ 解决方案:  

  • 确保 pages.json 配置正确  

  • 如果你用了全屏的 scroll-view,并且希望有下拉刷新,直接在 scroll-view 上做,别用页面级的


💥 坑2:scroll-view 不滚动

现象:设置了 scroll-y,但内容超出后依然不能滚动。原因没有给 scroll-view 固定高度。滚动的前提是:容器高度 < 内容高度。✅ 解决方案:  

.scroll-list {height100vh;           /* 固定视口高度,或 calc(100vh - 50px) *//* 或者使用 flex 布局父容器,给 scroll-view flex:1 */}

万能 flex 布局方案(推荐):  

<template><viewclass="page"><viewclass="header">头部</view><scroll-viewclass="scroll-view"scroll-y>      ...</scroll-view></view></template><style>.page {displayflex;flex-directioncolumn;height100vh;   /* 父容器固定高度 */}.header {height44px;}.scroll-view {flex1;         /* 剩余高度全部给 scroll-view */}</style>

💥 坑3:上拉加载触发了,但数据不满一屏,不应该触发啊?

页面级 onReachBottom:只要滚动到底部就触发,哪怕内容没占满一屏,只要用户试图上拉(触底)也会触发。解决方案:在加载更多逻辑里加判断:if (page >= totalPage) return;

scroll-view @scrolltolower:如果内容高度 <= scroll-view 高度,无法滚动,scrolltolower不会触发——这是正常的。不需要额外处理。


💥 坑4:数据重复加载

现象:下拉刷新和上拉加载同时触发,或者用户手速快连续触发了两次加载更多,导致数据重复。原因:没有加“锁”。✅ 解决方案:加一个 loading 状态,正在请求时不重复请求。

letisLoading=falseconstloadMore=async () => {if (isLoadingreturnif (page>=totalPagereturn// 没更多了isLoading=truetry {constres=awaitfetchData(page+1)list.value.push(...res.data)page.value++  } finally {isLoading=false  }}

💥 坑5:scroll-view 下拉刷新与页面下拉冲突

场景:页面既有页面级下拉刷新(通过 pages.json 配置),内部又有一个 scroll-view 下拉刷新。结果:两者都会触发,或者互相抢夺手势。✅ 解决方案二选一,不要混用。通常如果用了 scroll-view 下拉,就在 pages.json 里关掉 enablePullDownRefresh


💥 坑6:H5 端页面级下拉刷新不流畅

现象:H5 端下拉刷新动画有点卡,或者下拉时整个页面被拖拽。原因:浏览器自带的下拉回弹效果与 UniApp 的下拉刷新叠加。✅ 解决方案:H5 端建议使用 scroll-view + 自定义下拉刷新,或者使用 @dcloudio/uni-pull2refresh 插件。


六、一个真实案例:商品列表页,顶部有分类 tab

很多电商小程序都是这种结构:  

  • 顶部:轮播图/搜索框  

  • 中间:分类 tab  

  • 底部:商品列表(可滚动、下拉刷新、上拉加载)  

错误做法:用页面级滚动,把整个页面作为滚动容器。结果用户下拉时,把轮播图和 tab 都拉下来了,体验很怪。  

正确做法:  

  1. 轮播图 + tab 固定在上方,不参与滚动  

  2. 商品列表区域用 scroll-view 独立滚动,内部实现下拉刷新、上拉加载  

代码骨架:  

<template><viewclass="page"><!-- 固定头部 --><viewclass="fixed-header"><swiper>...</swiper><viewclass="tabs">...</view></view><!-- 可滚动列表 --><scroll-viewclass="goods-list"scroll-yrefresher-enabled:refresher-triggered="refreshing"@refresherrefresh="handleRefresh"@scrolltolower="loadMore"><goods-itemv-for="item in list"/><uni-load-more:status="loadMoreStatus"/></scroll-view></view></template>

完美解决。


七、总结:选型决策树

问自己三个问题:  

  1. 页面是否有固定头部/底部,且这些区域不参与滚动?→ 是:用 scroll-view(区域滚动)→ 否:走下一步  

  2. 是否在意页面级下拉刷新把整个页面都拉下来的效果?→ 在意:用 scroll-view→ 不在意:可考虑页面级(简单)  

  3. 列表数据量极大(几千条)?→ 是:推荐页面级滚动,性能更好(原生滚动)→ 否:两种皆可,按布局选  


八、最后的碎碎念

小老弟,下拉刷新和上拉加载就像吃饭喝水一样,是每个前端的基本功。不要畏惧 scroll-view 的高度计算,那是你从“只会写页面”到“会做布局”的必经之路。

花半小时把今天讲的几个坑手动复现一遍,再亲手修好它们。以后你面试被问到“怎么实现下拉加载”,不仅能回答上来,还能说出“页面级适合什么、scroll-view 适合什么、坑在哪”,这就碾压一票只会复制粘贴的人。

今天就到这儿,去练练手吧!遇到奇怪的问题,欢迎随时带着代码来敲我。

—— 一个曾经也被滚动折磨过的老前辈 🛋️

加油,未来的全栈大佬!💪如果你也对移动端跨端开发感兴趣,关注我,后续还有更多优质文章分享!

往期相关文章推荐

本站文章均为手工撰写未经允许谢绝转载:夜雨聆风 » Uniapp实现下拉刷新上拉加载更多功能开发实战教程

评论 抢沙发

6 + 6 =
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
×
订阅图标按钮