Varlet List 组件详解:无限滚动触底加载的用法与源码级原理

发布时间:2026/10/13 12:21:27

Varlet List 组件详解:无限滚动触底加载的用法与源码级原理 UI组件前端【免费下载链接】varletA Vue3 component library based on Material Design 2 and 3, supporting mobile and desktop.项目地址https://gitcode.com/gh_mirrors/va/varlet点击查看免费下载var-list是 Varlet基于 Material Design 2/3 的 Vue 3 组件库中用于实现无限滚动列表的核心组件。它通过监听滚动容器实现触底自动加载支持加载、失败、加载完成三种状态的自定义展示并可与下拉刷新PullRefresh、标签页TabItem等组件联动。本文基于组件官方文档与仓库源码系统讲解其使用方式、状态管理协议与触底检测的底层实现。组件能力总览根据 组件文档var-list提供以下核心能力无限滚动加载检测到滚动容器滚动到底部时自动触发load事件手动位置检查通过实例方法check手动触发位置检测触底时触发load事件状态展示内置加载状态loading、错误状态error、数据加载完成状态finished的展示区域且均支持通过插槽或文本属性自定义。组件源码位于 packages/varlet-ui/src/list/入口文件 index.ts 通过withInstall注册组件并导出listProps供按需引入与默认值定制使用。基本使用load 事件与 loading 状态协议触底加载的核心交互协议是当检测到滚动容器滚动到底部时触发load事件组件会同时将loading置为true加载结束时必须手动将loading置回false本轮加载才算结束。这是防止重复触发的关键——loading为true期间组件不会再次触发加载见下文源码分析。script setup import { ref } from vue const loading ref(false) const finished ref(false) const list ref([]) function load() { setTimeout(() { for (let i 0; i 20; i) { list.value.push(list.value.length 1) } loading.value false if (list.value.length 60) { finished.value true } }, 1000) } /script template var-list :finishedfinished v-model:loadingloading loadload var-cell :keyitem v-foritem in list List Item: {{ item }} /var-cell /var-list /template要点说明v-model:loading双向绑定加载状态触发load时组件内部会自动将其置为truefinished为只读属性表示数据已全部加载完成置为true后组件停止触发load并展示finished-text文案官方示例代码与仓库内演示 example/index.vue 中load的实现逻辑一致每次追加 20 条数据累计到 60 条后标记finished。加载失败error 状态与点击重试当数据请求失败时可用v-model:error手动置位错误状态。组件会展示错误提示文案点击该提示会由组件自动将error复位为false并重新触发load事件从而实现点击重试的交互。script setup import { ref } from vue const loading ref(false) const error ref(false) const list ref([]) function load() { setTimeout(() { if (list.value.length 40) { error.value true loading.value false return } for (let i 0; i 20; i) { list.value.push(list.value.length 1) } loading.value false }, 1000) } /script template var-list v-model:errorerror v-model:loadingloading loadload var-cell :keyitem v-foritem in list List Item: {{ item }} /var-cell /var-list /template点击重试的实现位于 List.vueerror插槽的默认渲染节点绑定了clickload而load方法内部会先调用onUpdate:error(false)复位错误状态再调用onUpdate:loading(true)进入加载态最后触发onLoad// packages/varlet-ui/src/list/List.vue function load() { call(props[onUpdate:error], false) call(props[onUpdate:loading], true) call(props.onLoad) }这个顺序保证了无论处于何种状态重新发起加载时错误提示都会被清除。自定义状态文案loading-text / finished-text / error-text三种状态区域均有默认文案且都可以通过属性覆盖属性含义默认值英文 localeloading-text加载中文案Loadingfinished-text加载完成文案No moreerror-text加载失败文案Load fail默认文案并非写死在组件中而是来自 locale 文件 locale/en-US.ts 中的listLoadingText、listFinishedText、listErrorText三个词条中文环境默认为加载中 / 没有更多了 / 加载失败见 locale/zh-CN.ts。List.vue 中的渲染优先级为显式传入的loadingText ?? (pt ? pt : t)(listLoadingText)即属性 LocaleProvider 注入文案 全局 locale 默认文案因此接入 LocaleProvider 的项目可统一切换文案而无需逐个设置属性。script setup import { ref } from vue const loading ref(false) const finished ref(false) const list ref([]) function load() { setTimeout(() { for (let i 0; i 20; i) { list.value.push(list.value.length 1) } loading.value false if (list.value.length 60) { finished.value true } }, 1000) } /script template var-list loading-textloading QAQ finished-textfinished ORZ error-texterror TNT :finishedfinished v-model:loadingloading loadload var-cell :keyitem v-foritem in list List Item: {{ item }} /var-cell /var-list /template与 PullRefresh 组合上拉加载 下拉刷新将var-list嵌套在var-pull-refresh中即可同时获得上拉加载与下拉刷新两种交互注意保证PullRefresh容器的高度不为0否则下拉手势无法正确识别script setup import { ref } from vue const refreshing ref(false) const loading ref(false) const list ref([]) function refresh() { setTimeout(() { console.log(refresh) refreshing.value false }, 2000) } function load() { setTimeout(() { for (let i 0; i 20; i) { list.value.push(list.value.length 1) } loading.value false }, 1000) } /script template var-pull-refresh v-modelrefreshing refreshrefresh var-list v-model:loadingloading loadload var-cell :keyd v-ford in listListItem {{ d }}/var-cell /var-list /var-pull-refresh /template该组合在 PullRefresh 文档 的组合一节中同样被推荐为下拉刷新 无限滚动的标准方案。源码解析触底检测是如何实现的阅读 List.vue 可以完整还原触底加载的三条技术路径。1. 定位滚动容器getParentScroller组件挂载时onSmartMounted调用getParentScroller(listEl.value)向上遍历父节点找到最近一个overflow-y为scroll或auto的元素作为监听目标若一直找不到则回退为window。实现位于 utils/elements.tsexport function getParentScroller(el: HTMLElement): HTMLElement | Window { let element el while (element) { if (!element.parentNode) { break } element element.parentNode as HTMLElement if (element document.body || element document.documentElement) { break } const scrollRE /(scroll|auto)/ const { overflowY, overflow } getStyle(element) if (scrollRE.test(overflowY) || scrollRE.test(overflow)) { return element } } return window }因此var-list既可以放在页面级滚动window滚动中也可以放在局部滚动容器如固定高度 overflow-y: auto的面板中二者都能正确触发触底检测。组件卸载时通过onSmartUnmounted(removeScrollerListener)移除scroll监听避免内存泄漏。2. 触底判定detector 元素与 offset组件模板末尾始终渲染一个检测节点div :classn(detector)配合 list.less 中width: 100%的零高度样式它始终位于列表内容的最底部。触底判定逻辑如下// packages/varlet-ui/src/list/List.vue function isReachBottom() { const { bottom: containerBottom } getRect(scroller) const { bottom: detectorBottom } getRect(detectorEl.value!) // The fractional part of the detectorBottom when bottoming out overflows return Math.floor(detectorBottom) - toPxNum(props.offset) containerBottom }其判定条件是detector 的可视区底部坐标减去offset后不超过滚动容器的可视区底部坐标。源码注释特意指出Math.floor的存在是修复触底时底部坐标溢出小数部分的边界问题——即浮点误差可能让判定多触发一次向下取整可消除该偏差。关于offset其类型是string | number默认0表示距离底部多远时提前触发加载预加载距离。从源码看字符串值会经过 toPxNum 统一换算支持10、10、10px以及vw/vh/rem等单位因此传入offset200可实现在距底部 200px 时提前加载下一页。3. 检查的触发时机与互斥守卫check是位置检查的入口也是组件实例暴露的方法存在三类触发来源// packages/varlet-ui/src/list/List.vue watch(() [props.loading, props.error, props.finished], check) onSmartMounted(() { scroller getParentScroller(listEl.value!) scroller.addEventListener(scroll, check) if (props.immediateCheck) { check() } }) async function check() { await nextTick() if (props.loading || props.finished || props.error || tabItem?.current.value false || !isReachBottom()) { return } load() }滚动监听滚动容器每次scroll事件都会调用check无节流依赖守卫条件短路初始化检查immediate-check默认true挂载后立即check一次——若首屏内容不足一屏初始即触底会立刻触发首次加载避免用户拉不动状态变化重检loading/error/finished任一变化都会重新check。例如加载完成追加数据后若仍触底会自动继续加载下一页。守卫条件loading || finished || error || tabItem?.current.value false || !isReachBottom()保证了同一时间只有一个进行中的加载loading互斥加载完成finished后不再响应任何触底处于错误态时等待用户点击重试而不是静默重发嵌套在TabItem中且该页签不可见时不加载见下节。check内部await nextTick()后再取坐标确保在 DOM 更新如新数据渲染、状态区显隐之后再计算避免基于过期布局误判。与 TabItem 联动的隐藏机制从源码结构看var-list与var-tab-item存在内置联动list/provide.ts 通过useTabItem从父级TabItem注入 provider并在 List.vue 中监听tabItem.currentconst { tabItem, bindTabItem } useTabItem() if (tabItem) { watch(() tabItem.current.value, check) }这意味着当var-list位于var-tabs-items的某个var-tab-item内时隐藏页签中的列表不会触发加载守卫条件tabItem?.current.value false切换到该页签时current变为true会自动check一次若已触底则加载。测试用例 index.spec.js 的load event while list in tab-item验证了这一行为初始状态下只有激活页签的load被调用一次切换到第二个页签后其load才被触发。使用var-tabs-itemsvar-tab-item承载多个分页列表时这一机制无需任何额外配置即可生效。使用注意滚动容器的识别陷阱官方文档Be Careful一节指出了一条重要的 CSS 边界问题触底检测依赖监听滚动容器的 scroll 事件。滚动容器是指overflow-y为auto或scroll的最近元素。当把某元素的overflow-x设置为非visible的值时浏览器会将该元素的overflow-y自动固化为auto以维持 BFC 结构——这会被误识别为滚动容器应避免。对照 getParentScroller 的实现可以确认它只检查overflow-y和overflow两个计算样式/(scroll|auto)/命中即返回。因此若列表外层存在一个overflow-x: hidden浏览器会把overflow-y计算为auto的包裹元素且该元素出现在body之前var-list就会把它的父级而非真正的滚动目标当作 scroller导致触底检测失效。实践建议在列表到body的路径上避免出现非visible的overflow-x声明。API 参考PropsProp说明类型默认值v-model:loading加载状态booleanfalsev-model:error错误状态booleanfalseimmediate-check初始化时是否立即进行位置检测booleantruefinished是否加载完成booleanfalseoffset距底部触发距离string | number0loading-text加载中文案stringLoadingfinished-text加载完成文案stringNo moreerror-text加载失败文案stringLoad fail默认值以 props.ts 源码为准immediateCheck默认true、offset默认0其余布尔状态默认false文案默认值来自 locale/en-US.ts。Methods方法说明参数返回值check触发位置检查触底时触发load事件--check通过setup的返回值暴露在组件实例上List.vue适合在数据手动更新、路由切换后强制重检一次是否触底。Events事件说明参数load触底时触发-Slots插槽名说明插槽参数default列表内容-loading加载中内容-error加载失败内容-finished加载完毕内容-四个插槽在 List.vue 模板 中均为条件渲染loading为真时渲染loading插槽默认内容是文案 var-loading迷你转圈finished/error同理。需要完全自定义状态区 UI例如替换为骨架屏或自定义按钮时使用对应命名插槽即可。样式变量组件样式定义在 list.less通过 CSS 变量控制三个状态区的外观可配合 StyleProvider 组件 定制变量名默认值--list-loading-height50px--list-finished-height50px--list-error-height50px--list-loading-color#888--list-finished-color#888--list-error-color#888--list-loading-font-sizevar(--font-size-md)--list-finished-font-sizevar(--font-size-md)--list-error-font-sizevar(--font-size-md)例如将加载区调矮并换色:root { --list-loading-height: 40px; --list-loading-color: var(--color-primary); }测试用例对行为的佐证单元测试tests/index.spec.js 从三个层面验证了上述机制触底触发mockgetBoundingClientRect返回bottom: 0即 detector 已触底后onLoad与onUpdate:loading各被调用 1 次验证了触底 → 自动置 loading → 触发 load的完整协议三种状态区分别以loading/error/finished为真时渲染对应的.var-list__loading/.var-list__error/.var-list__finished节点并展示自定义文案置回false后节点移除TabItem 联动两个页签各含一个var-list仅激活页签的列表触发加载切换页签后新页签列表才触发加载。小结var-list的用法可以浓缩为一条状态协议监听load事件发起请求 → 请求结束置loading false→ 数据耗尽置finished true→ 请求异常置error true并等待点击重试。理解其源码实现后还有三点值得记住滚动容器通过overflow-y自动向上查找、触底判定依赖底部的零高度 detector 元素与offset预加载距离、overflow-x的非visible值会污染滚动容器识别。掌握这些细节可以在移动端信息流、分页接口、多页签列表等场景下稳定落地无限滚动加载。赞分享UI组件前端【免费下载链接】varletA Vue3 component library based on Material Design 2 and 3, supporting mobile and desktop.项目地址https://gitcode.com/gh_mirrors/va/varlet点击查看免费下载相关推荐Naive UI 无限滚动 Infinite Scroll 组件实战指南滚动到底自动加载的实现原理与参数详解Naive UI 无限滚动 Infinite Scroll 组件实战指南滚动到底自动加载的实现原理与参数详解 导读 本文围绕 Naive UI 组件库中的 n前端UI组件Naive UI 无限滚动组件 Infinite Scroll 实战指南props 用法与源码原理Naive UI 无限滚动组件 Infinite Scroll 实战指南props 用法与源码原理 自 Naive UI 2.38.2 版本起组件库内置了前端UI组件javascript.info 无限滚动页面实战用 getBoundingClientRect 与 scroll 事件实现触底自动加载javascript.info 无限滚动页面实战用 getBoundingClientRect 与 scroll 事件实现触底自动加载 无限滚动endles文档/教程前端上一篇OpenNI API完全手册开发者必备的函数与接口速查指南下一篇JS The Right Way依赖管理npm与yarn的最佳实践创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/10/13 12:16:24

光储充换电站分时电价与用户负荷互动双层优化模型及Matlab复现

光储充换电站这个概念最近两年有多热,看看知网就明白了——尤其是“分时电价”和“用户负荷”两个词,几乎成了这类论文标题的流量密码。但真正动手复现过的人都知道,大多数公开代码只给你一张固定电价表去调度,用户完全是被动接收…

2026/10/13 13:21:41

色彩规范落地指南:从团队协作到对比度检查的完整推演

做网页设计这些年,我越来越确认一件事:色彩规范不是一个色板文档,而是一套团队的“公共决策协议”。没有这套协议,设计稿里每个人都是自己的“配色大师”,开发还原全靠肉眼对齐,最后页面上永远充斥着四个不…

2026/10/13 13:21:41

用原生JS打造微博信息流Demo:DOM操作与数据驱动实战

很多人第一次看到“js微博制作”这个标题,第一反应是要做一个什么大平台,其实完全不是。它就只是一个前端练手项目:用JavaScript做一个类似微博信息流风格的网页Demo。核心是你要有能力把一个输入框里的话,实时渲染到页面上&#…

2026/10/13 13:21:41

Flutter 自定义底部导航栏:从原理到仿闲鱼动效完整实现

做 Flutter 仿闲鱼底部导航栏,这个标题我一看就来劲。说实话,Flutter 官方提供的 BottomNavigationBar 应付常规业务场景绰绰有余,但想把闲鱼那种中间发布按钮凸起、选中项下方有小横条滑动、点击发布按钮还能旋转变成关闭态的交互还原出来&a…

2026/10/13 13:21:41

vLLM多模型并发部署实战:DeepSeek-R1与Qwen3显存分配与调度调优

1. 为什么多模型并发部署是个"看起来简单、做起来要命"的活先把场景摆出来。你手里有一台或者几台推理服务器,显卡可能是 A100、H800、L40S 或者消费级的 4090 集群,业务侧要求同时对外提供 DeepSeek-R1 和 Qwen3 两个系列模型的推理服务。Dee…

2026/10/13 13:21:41

双狐软件构造成图全流程:从数据加载到批量出图的避坑指南

简介:这份资源围绕双狐软件在地质成图与地球物理勘探中的应用展开,面向地质、石油工程领域的技术人员及需要编制构造图的学习者,帮助解决从基础数据准备到成图输出的完整流程问题。压缩包内共1个PDF文件,约646KB,以图文…

2026/10/13 13:16:40

对称二叉树判断:递归与迭代的镜像匹配核心逻辑详解

1. 先搞清楚这道题到底在考什么1.1 题目定位与常见误区对称二叉树这道题在 LeetCode 上编号 101,属于二叉树章节的经典入门题,同时也是“热门 100 题”里的常客。我印象里几乎所有大厂面试题库里都有它,而且往往不是考你能不能写出来&#xf…

2026/10/13 5:19:27

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”,而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gem…

2026/10/12 12:01:26

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里,有A同学跑来问我:选什么毕设题目最稳妥,既能让评审老师觉得工作量够,又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/12 12:54:07

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友,在看完一堆“Hello World”和基础组件之后,大概率都会撞上同一堵墙:StatefulWidget 里那堆 initState、build、dispose 方法,到底什么时候被调用?为什么顺序是那样?在里面到…

2026/10/13 0:04:41

基于Triton部署YOLO目标检测:从训练脚本到线上服务

简介:这份资源面向希望将目标检测模型落地到生产环境的深度学习开发者与算法工程师,聚焦于用 Triton Inference Server 部署 YOLOv9 的完整实战方案,解决从训练权重到线上推理服务之间的工程化难题。压缩包共 16 个文件,约 886KB&…

2026/10/13 0:04:41

手把手教你云端部署Qwen-Image-2.1:从GPU选型到在线文生图服务

Qwen-Image-2.1 这个开源的图像生成模型,最近在内容创作和 AI 应用圈子里热度相当高。它属于通义 Qwen 系列里的视觉生成分支,让我眼前一亮的是它对中文长文本的理解能力——以前用那些老方案生成带中文文字的图,十个里九个是“乱码印刷体”&…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑