尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
uni-app x waterflow 瀑布流组件完整指南:从属性配置到 C++ 回收复用原理
uni-app x waterflow 瀑布流组件完整指南从属性配置到 C 回收复用原理【免费下载链接】uni-appA cross-platform framework using Vue.js项目地址: https://gitcode.com/gh_mirrors/un/uni-appwaterflow 是 uni-app x 中面向 App 平台Android / iOS / HarmonyOS的高性能瀑布流长列表组件底层与 list-view 采用同构的滑动回收 复用机制配合唯一的合法子组件 flow-item 实现多列交错布局。读完本文你将掌握 waterflow / flow-item 的全部属性与事件用法、自定义下拉刷新与加载更多插槽的完整实现以及基于src/uni_modules/uni-waterflow中 C 回收复用源码的底层布局与渲染原理。组件类型UniWaterFlowElementwaterflow/ UniFlowItemElementflow-item组件定位与适用场景waterflow 组件专用于多元素瀑布流长列表场景。在 App 中waterflow 底层实现与 list-view 底层实现方式基本一致仅子组件排列方式存在差异list-view 是单列纵向排列而 waterflow 按cross-axis-count拆分为多列。子组件滑动出屏幕后会及时回收复用因此性能优于 scroll-view。使用 waterflow 必须遵守以下约束waterflow 暂时只支持 flow-item 组件为子组件其他组件不可见waterflow 只支持竖向滚动不支持横向滚动鸿蒙平台 API 20 及以上版本支持滚动相关事件API 18 及以上版本支持 load-more 插槽。从源码看组件声明位于 src/uni_modules/uni-waterflow/components/waterflow/waterflow.uvue其模板本质上是在scroll-view之上注入recycleWaterflowCpp这一 C 模块langcpp由 recycle_waterflow.h 中的RecycleWaterflow类承担核心的列表管理、布局计算与回收复用逻辑这正是性能优于 scroll-view的源码依据。兼容性| Web | 微信小程序 | Android | iOS | HarmonyOS(VDOM) | HarmonyOS(Vapor) | | :- | :- | :- | :- | :- | :- | | x | x | 4.41 | 4.41 | 4.81 | 5.02 |即该组件不支持 Web 与微信小程序仅面向 App 三端其中 iOS / Android 自 4.41 起支持HarmonyOS(VDOM) 自 4.81 起支持HarmonyOS(Vapor) 自 5.02 起支持。组件的 uni_modules 元数据package.json中也明确将web、mp-weixin平台标记为false与文档兼容性表一致。waterflow 属性详解| 名称 | 类型 | 默认值 | 描述 | | :- | :- | :- | :- | | cross-axis-count | number | string | 2 | 交叉轴元素数量即瀑布流列数 | | main-axis-gap | number | string | 0 | 主轴方向间隔纵向间隔 | | cross-axis-gap | number | string | 0 | 交叉轴方向间隔列间间隔 | | max-cross-axis-extent | number | string | 0 | flow-item 交叉轴方向最大尺寸iOS(Vapor) 5.11、HarmonyOS(Vapor) 5.02设置后按该尺寸自动计算列数 | | padding | Array | [0,0,0,0] | 长度为 4 的数组按 top、right、bottom、left 顺序指定内边距。蒸汽模式Vapor已废弃此属性推荐使用 css padding 属性 | | associative-container | string | | 关联的滚动容器合法值为nested-scroll-view嵌套滚动 | | bounces | boolean | string | true | 控制是否回弹效果 | | android-overscroll | boolean | string | false | Android 平台是否开启弹簧回弹效果开启后默认下拉刷新使用圆弧 loading 样式推动页面内容否则默认下拉刷新使用 Android 系统悬浮圆环样式不推动页面内容。Android(Vapor) 5.21 | | android-scrollbar-draggable | boolean | string | false | Android 平台是否允许拖动滚动条快速定位滚动内容当前暂仅支持竖向滚动。Android(Vapor) 5.26 | | android-refresher-color | string(string.ColorString) | | 设置 Android 平台默认下拉刷新样式的前景颜色优先级高于 refresher-default-style。Android(Vapor) 5.25 | | upper-threshold | number | 50 | 距顶部多远时单位 px触发 scrolltoupper 事件 | | lower-threshold | number | 50 | 距底部多远时单位 px触发 scrolltolower 事件 | | scroll-top | number | 0 | 设置竖向滚动条位置 | | show-scrollbar | boolean | string | true | 控制是否出现滚动条 | | scroll-into-view | string(string.IDString) | | 值应为某子元素 idid 不能以数字开头滚动到该元素起始位置。Android(VDOM)/iOS(VDOM) 4.41、Android(Vapor)/iOS(Vapor) 5.25、HarmonyOS(Vapor) 5.25 | | scroll-with-animation | boolean | string | false | 是否在设置滚动条位置时使用滚动动画 | | refresher-enabled | boolean | string | false | 开启下拉刷新暂时不支持横向刷新 | | refresher-threshold | number | 45 | 设置下拉刷新阈值仅refresher-default-style none自定义样式下生效 | | refresher-max-drag-distance | number | | 设置下拉最大拖拽距离单位 px默认是下拉刷新控件高度的 2.5 倍 | | refresher-default-style | string | black | 设置下拉刷新默认样式支持 black | white | nonenone 表示不使用默认样式 | | refresher-background | string(string.ColorString) | transparent | 设置下拉刷新区域背景颜色。Android 平台下当 android-overscrollfalse 时默认下拉刷新使用悬浮圆环样式此时背景默认不再透明而是由 refresher-default-style 决定 | | refresher-triggered | boolean | string | false | 设置当前下拉刷新状态true 表示已触发false 表示未触发 | | enable-back-to-top | boolean | string | false | iOS 点击顶部状态栏滚动条返回顶部只支持竖向。iOS 4.41、HarmonyOS(Vapor) 5.02 | | custom-nested-scroll | boolean | string | false | 子元素是否开启嵌套滚动将滚动事件与父元素协商处理。Android 4.41、iOS 不支持 | | scroll-left | number | | 设置横向滚动条位置 | | nested-scroll-child | string | | 嵌套滚动子元素标识 |以上属性的声明与默认值可直接在源码中核对waterflow.uvue 的 defineProps 定义。源码中所有布尔属性均以[Boolean, String]联合类型声明即支持:bouncestrue与bouncestrue两种写法crossAxisCount、mainAxisGap、crossAxisGap、maxCrossAxisExtent支持 number 与 string源码在realCrossAxisCount等 computed 中通过parseInt/parseFloat统一转换为数值后交给 C 层。max-cross-axis-extent 与列数的自动计算这是一个容易混淆的属性。从源码 waterflow.uvue 第 144-149 行 可以看到const realCrossAxisCount computed(() { if (props.maxCrossAxisExtent ! 0 props.maxCrossAxisExtent ! 0) { return 0; } return parseInt(props.crossAxisCount); })当设置了max-cross-axis-extent非 0时传入 C 层的crossAxisCount被置为 0表示列数不由开发者指定而是由RecycleWaterflow::resolveCrossAxisCount()见 recycle_waterflow.h根据容器宽度与每项最大交叉轴尺寸自动求解从而实现按最大项宽自适应列数的效果。未设置该属性时列数严格等于cross-axis-count。associative-container 的属性描述| 合法值 | 描述 | | :- | :- | | nested-scroll-view | 嵌套滚动 |refresher-default-style 的属性描述| 合法值 | 描述 | | :- | :- | | black | 深颜色雪花样式 | | white | 浅白色雪花样式 | | none | 不使用默认样式 |waterflow 事件详解UniRefresherEvent下拉刷新相关事件refresherpulling、refresherrefresh、refresherrestore、refresherabort的事件对象继承自 UniEvent。属性值| 名称 | 类型 | 必填 | | :- | :- | :- | | detail | UniRefresherEventDetail | 是 |detail 属性描述| 名称 | 类型 | 必备 | | :- | :- | :- | | dy | number | 是 |dy表示下拉刷新的拖拽距离示例代码中通过e.detail.dy 45判断是否达到触发阈值见下文完整示例。UniScrollToUpperEvent滚动到顶部/左边触发scrolltoupper的事件对象继承自 UniEvent。属性值| 名称 | 类型 | 必填 | | :- | :- | :- | | detail | UniScrollToUpperEventDetail | 是 |detail 属性描述| 名称 | 类型 | 必备 | 描述 | | :- | :- | :- | :- | | direction | string | 是 | 滚动方向 top 或 left |UniScrollToLowerEvent滚动到底部/右边触发scrolltolower的事件对象继承自 UniEvent。属性值| 名称 | 类型 | 必填 | | :- | :- | :- | | detail | UniScrollToLowerEventDetail | 是 |detail 属性描述| 名称 | 类型 | 必备 | 描述 | | :- | :- | :- | :- | | direction | string | 是 | 滚动方向 bottom 或 right |UniScrollEventscroll、scrollend的事件对象继承自 UniEvent。属性值| 名称 | 类型 | 必填 | | :- | :- | :- | | detail | UniScrollEventDetail | 是 |detail 属性描述| 名称 | 类型 | 必备 | 描述 | | :- | :- | :- | :- | | scrollTop | number | 是 | 竖向滚动的距离 | | scrollLeft | number | 是 | 横向滚动的距离 | | scrollHeight | number | 是 | 滚动区域的高度 | | scrollWidth | number | 是 | 滚动区域的宽度 | | deltaY | number | 是 | 当次滚动事件竖向滚动量 | | deltaX | number | 是 | 当次滚动事件横向滚动量 |自定义下拉刷新样式waterflow 组件有默认的下拉刷新样式使用方式与 scroll-view 的自定义下拉刷新相同详见 scroll-view 自定义下拉刷新。uni-ui x 提供了开源的自定义下拉刷新组件 uni-refresh-box 可参考体验。实现步骤设置refresher-default-style属性为none不使用默认样式设置 flow-item 定义自定义下拉刷新元素并声明为slotrefresher需要设置刷新元素宽高信息否则可能无法正常显示通过组件提供的refresherpulling、refresherrefresh、refresherrestore、refresherabort下拉刷新事件调整自定义下拉刷新元素实现预期效果。template waterflow refresher-default-stylenone :refresher-enabledtrue :refresher-triggeredrefresherTriggered refresherpullingonRefresherpulling refresherrefreshonRefresherrefresh refresherrestoreonRefresherrestore styleflex:1 flow-item v-fori in 10 classcontent-item type1 text classtextitem-{{i}}/text /flow-item !-- 自定义下拉刷新元素 -- flow-item slotrefresher classrefresh-box type2 text classtip-text{{text[state]}}/text /flow-item /waterflow /template注意目前自定义下拉刷新元素不支持放在 waterflow 的首个子元素位置上可能无法正常显示。加载更多load-more由于 waterflow 组件是多列的单个 flow-item 组件无法占用一整行加载更多展示不太友好因此提供了slotload-more属性配置waterflow 组件会特殊处理最后一个 flow-item 子组件如果配置了slotload-more则显示一整行。template waterflow styleflex:1 flow-item v-fori in 10 classcontent-item type1 text classtextitem-{{i}}/text /flow-item flow-item slotload-more idloadmore type3 classload-more-box text加载更多/text /flow-item /waterflow /template注意slotload-more仅对 waterflow 最后一个 flow-item 子组件生效。从 waterflow.uvue 模板 可以看到普通内容插槽、refresher插槽、load-more插槽按顺序放置在 scroll-view 内部其中load-more插槽被排在最后与仅最后一个子组件生效的约定对应。嵌套模式scroll-view 开启嵌套模式后waterflow 可作为内层滚动视图与外层 scroll-view 实现嵌套滚动。设置内层 waterflow 的associative-container属性为nested-scroll-view即可开启内层 waterflow 与外层 scroll-view 的嵌套滚动。仓库示例 src/pages/template/long-waterflow-nested/long-waterflow-nested.uvue 与配套测试 long-waterflow-nested.test.js 即用于验证这种外层 scroll-view 内层 waterflow嵌套滚动场景。flow-item唯一的合法子组件组件类型UniFlowItemElementflow-item 是 waterflow 组件的唯一合法子组件配合 waterflow 内部业务实现 flow-item 回收复用从而实现高性能的瀑布流长列表组件。属性| 名称 | 类型 | 默认值 | 描述 | | :- | :- | :- | :- | | type | number | 0 | 对应 flow-item 的类型。waterflow 将对同类型条目进行复用所以合理的类型拆分可以很好地提升 waterflow 性能 | | item-key | string | number | | 条目唯一 key必填源码中required: true | | item-height | number | | 条目高度可选源码默认 0见 flow-item.uvue |flow-item 组件宽度由 waterflow 的cross-axis-count属性决定所以设置与宽度相关的 style 样式都不生效。flow-item 宽度计算公式((waterflow的width - waterflow的左右padding值域 - waterflow的border-width的值域) - (cross-axis-count - 1) * cross-axis-gap) / cross-axis-count即列宽 容器内容区宽度 − 列间总间隔÷ 列数。C 层RecycleWaterflow中的resolveCrossAxisSize(int count)recycle_waterflow.h与updateCrossAxisMetrics()正是在运行期计算并下发这一列宽flow-item 组件本身以position: absolute; width: 0px;起步由原生层通过translate与尺寸调整精确排版见 flow-item.uvue 模板。flow-item 复用机制waterflow 与 list-view 实现机制几乎一致所以 flow-item 与 list-item 组件复用机制是一样的请参考 list-item 复用机制。子组件flow-item 支持所有组件作为其子组件。源码级原理C 回收复用引擎waterflow 之所以性能优于 scroll-view核心在于 recycle_waterflow.cpp 与 recycle_waterflow.h 实现的回收复用引擎以下均从源码结构推断条目元信息管理RecycleWaterflow内部维护std::vectorItemInfo list与std::unordered_mapstd::string, ItemInfo* keyItemMap两级结构ItemInfo记录index / key / size / offsetX / offsetY / typerecycle_waterflow.h 第 135-146 行。keyItemMap用于 O(1) 定位条目这是回收复用的索引基础。渲染区间render range组件不会一次性渲染全部条目而是通过updateRenderListForward()/updateRenderListBackward()按滚动方向增量维护renderRangeStart / renderRangeLength只实例化可视区附近的flow-item节点其余条目仅以元数据形式存在于list中。前端renderRange通过updateRenderInfo暴露waterflow.uvue 第 178-182 行。快速滚动模式fast scroll源码中有enterFastScrollMode()、leaveFastScrollMode()、prepareFastScroll()与fastScrollOffset字段注释明确说明快速滚动过程中不渲染新的 item旧 item 通过 offset fastScrollOffset 显示在要渲染的区域滚动条快速滚动时内容缓慢滚动通过translate将缓慢滚动的内容持续显示在可视区域。Android 与其它平台的加速度阈值不同Android 为fastScrollVelocity10 / fastScrollDeltaLimit80其余平台为15 / 120这是考虑到安卓设备性能参差所做的适配。尺寸观察与重排RecycleFlowItem持有UniResizeObserver *resizeObserver通过startObserve() / stopObserve()观察条目尺寸变化并调用updateItemSize回写RecycleWaterflow::updateItemSize()会触发updateRenderListOnItemSizeChange()重新排版后续条目——这也解释了文档中子组件高度动态变化会导致重新排版、出现短暂错乱的注意事项。资源销毁前端onBeforeUnmount中通过NATIVE_CHANNEL_ACTIONS.DESTROY通知 C 层清理waterflow.uvue 第 189-193 行RecycleWaterflow也维护destroyed标志位防止销毁后继续回调。App 平台注意事项与性能优化waterflow 的性能优化方式与 list-view 相同详见 list-view 列表性能优化。waterflow 组件默认高度是计算后瀑布流高度无法滚动如需要滚动要设置高度小于瀑布流高度推荐配置flex:1。示例页面中均采用styleflex:1的写法。waterflow 组件在排版完毕 UI 展示后如果子组件高度发生变化则会重新排版flow-item 组件的 UI 会出现短暂的错乱和位置位移建议控制子元素高度固定避免动态修改高度。典型场景场景一image 组件设置modewidthFix加载的网络图片会出现异步图片下载动态修改高度的情况场景二rich-text 组件 nodes 节点内容存在网络图片出现异步加载情况会动态修改高度。waterflow 组件的 overflow 属性不支持配置visible。仓库中专门提供了两个针对性的示例高度自适应场景见 src/pages/component/waterflow/waterflow-fit-height.uvue配合image modewidthFix与网络图片数据验证图片异步加载下的表现纯性能压测场景见 src/pages/template/waterflow-perf/waterflow-perf.uvue数据源见 mock-data.uts。自动化测试 src/pages/component/waterflow/waterflow.test.js 覆盖了列数、间距、padding 修改与单列模式testModifyWaterflowProps/testModifyWaterflowSingleRow等用例。完整示例以下为完整可运行的.uvue示例含脚本、模板与样式演示列数/间距调节、滚动控制、下拉刷新、加载更多与 scroll-into-view 跳转script setup languts type ScrollEventTest { type : string; target : UniElement | null; currentTarget : UniElement | null; direction ?: string } type flowItemData { height : number, text : string } type DataType { refresher_triggered_boolean: boolean; refresher_enabled_boolean: boolean; scroll_with_animation_boolean: boolean; show_scrollbar_boolean: boolean; bounces_boolean: boolean; upper_threshold_input: number; lower_threshold_input: number; scroll_top_input: number; scroll_left_input: number; refresher_background_input: string; scrollData: ArrayflowItemData; size_enum: ItemType[]; scrollIntoView: string; refresherrefresh: boolean; refresher_default_style_input: string; text: string[]; state: number; reset: boolean; isScrollTest: string; isScrolltolowerTest: string; isScrolltoupperTest: string; scrollDetailTest: UniScrollEventDetail | null; scrollEndDetailTest: UniScrollEventDetail | null; cross_axis_count: number; main_axis_gap: number; cross_axis_gap: number; waterflowPadding: Arraynumber; isLoadingMore: boolean; hasMore: boolean; osHarmonySDKAPIVersion: number; } import { ItemType } from /components/enum-data/enum-data-types const data reactive({ refresher_triggered_boolean: false, refresher_enabled_boolean: false, scroll_with_animation_boolean: false, show_scrollbar_boolean: false, bounces_boolean: true, upper_threshold_input: 50, lower_threshold_input: 50, scroll_top_input: 0, scroll_left_input: 0, refresher_background_input: #FFF, scrollData: [], size_enum: [{ value: 0, name: item---0 }, { value: 3, name: item---3 }], scrollIntoView: , refresherrefresh: false, refresher_default_style_input: black, text: [继续下拉执行刷新, 释放立即刷新, 刷新中, ], state: 3, reset: true, // 自动化测试 isScrollTest: , isScrolltolowerTest: , isScrolltoupperTest: , scrollDetailTest: null, scrollEndDetailTest: null, cross_axis_count: 2, main_axis_gap: 2, cross_axis_gap: 2, waterflowPadding: [10, 5, 10, 5], isLoadingMore: false, hasMore: true, osHarmonySDKAPIVersion: 0 } as DataType) const waterflowRef refUniWaterFlowElement | null(null) // #ifdef APP-HARMONY const deviceInfo uni.getDeviceInfo() data.osHarmonySDKAPIVersion deviceInfo.osHarmonySDKAPIVersion // #endif onLoad(() { // 静态瀑布流数据 data.scrollData [ { height: 300, text: item---0 }, { height: 150, text: item---1 }, { height: 120, text: item---2 }, { height: 100, text: item---3 }, { height: 100, text: item---4 }, { height: 150, text: item---5 }, { height: 140, text: item---6 }, { height: 190, text: item---7 }, { height: 160, text: item---8 }, { height: 120, text: item---9 }, { height: 109, text: item---10 }, { height: 102, text: item---11 }, { height: 123, text: item---12 }, { height: 156, text: item---13 }, { height: 177, text: item---14 }, { height: 105, text: item---15 }, { height: 110, text: item---16 }, { height: 90, text: item---17 }, { height: 130, text: item---18 }, { height: 140, text: item---19 }, ] as ArrayflowItemData }) const waterflow_click () { console.log(组件被点击时触发) } const waterflow_touchstart () { console.log(手指触摸动作开始) } const waterflow_touchmove () { console.log(手指触摸后移动) } const waterflow_touchcancel () { console.log(手指触摸动作被打断如来电提醒弹窗) } const waterflow_touchend () { console.log(手指触摸动作结束) } const waterflow_tap () { console.log(手指触摸后马上离开) } const waterflow_longpress () { console.log(如果一个组件被绑定了 longpress 事件那么当用户长按这个组件时该事件将会被触发。) } const waterflow_refresherpulling (e : RefresherEvent) { console.log(下拉刷新控件被下拉) if (data.reset) { if (e.detail.dy 45) { data.state 1 } else { data.state 0 } } } const waterflow_refresherrefresh () { console.log(下拉刷新被触发 ) data.refresherrefresh true data.refresher_triggered_boolean true data.state 2 data.reset false; setTimeout(() { data.refresher_triggered_boolean false }, 1500) } const waterflow_refresherrestore () { data.refresherrefresh false data.state 3 data.reset true console.log(下拉刷新被复位) } const waterflow_refresherabort () { console.log(下拉刷新被中止) } // 自动化测试专用由于事件event参数对象中存在循环引用在ios端JSON.stringify报错自动化测试无法page.data获取 const checkEventTest (e : ScrollEventTest, eventName : String) { const isPass e.type eventName e.target instanceof UniElement e.currentTarget instanceof UniElement; const result isPass ? ${eventName}:Success : ${eventName}:Fail; switch (eventName) { case scroll: data.isScrollTest result break; case scrolltolower: data.isScrolltolowerTest result -${e.direction} break; case scrolltoupper: data.isScrolltoupperTest result -${e.direction} break; default: break; } } const waterflow_scrolltoupper (e : UniScrollToUpperEvent) { console.log(滚动到顶部/左边会触发 scrolltoupper 事件 direction e.detail.direction) checkEventTest({ type: e.type, target: e.target, currentTarget: e.currentTarget, direction: e.detail.direction, } as ScrollEventTest, scrolltoupper) } const waterflow_scrolltolower (e : UniScrollToLowerEvent) { console.log(滚动到底部/右边会触发 scrolltolower 事件 direction e.detail.direction) checkEventTest({ type: e.type, target: e.target, currentTarget: e.currentTarget, direction: e.detail.direction, } as ScrollEventTest, scrolltolower) } const waterflow_scroll (e : UniScrollEvent) { console.log(滚动时触发event.detail , e.detail) data.scrollDetailTest e.detail checkEventTest({ type: e.type, target: e.target, currentTarget: e.currentTarget } as ScrollEventTest, scroll) } const waterflow_scrollend (e : UniScrollEvent) { console.log(滚动结束时触发, e.detail) data.scrollEndDetailTest e.detail checkEventTest({ type: e.type, target: e.target, currentTarget: e.currentTarget } as ScrollEventTest, scrollend) } const flow_item_click () { console.log(flow-item组件被点击时触发) } const flow_item_touchstart () { console.log(手指触摸flow-item组件动作开始) } const flow_item_touchmove () { console.log(手指触摸flow-item组件后移动) } const flow_item_touchcancel () { console.log(手指触摸flow-item组件动作被打断如来电提醒弹窗) } const flow_item_touchend () { console.log(手指触摸flow-item组件动作结束) } const flow_item_tap () { console.log(手指触摸flow-item组件后马上离开) } const flow_item_longpress () { console.log(flow-item组件被绑定了 longpress 事件那么当用户长按这个组件时该事件将会被触发。) } const change_refresher_triggered_boolean (checked : boolean) { data.refresher_triggered_boolean checked } const change_refresher_enabled_boolean (checked : boolean) { data.refresher_enabled_boolean checked } const change_scroll_with_animation_boolean (checked : boolean) { data.scroll_with_animation_boolean checked } const change_show_scrollbar_boolean (checked : boolean) { data.show_scrollbar_boolean checked } const change_bounces_boolean (checked : boolean) { data.bounces_boolean checked } const confirm_upper_threshold_input (value : number) { data.upper_threshold_input value } const confirm_lower_threshold_input (value : number) { data.lower_threshold_input value } const confirm_scroll_top_input (value : number) { data.scroll_top_input value } const confirm_scroll_left_input (value : number) { data.scroll_left_input value } const confirm_refresher_background_input (value : string) { data.refresher_background_input value } const item_change_size_enum (index : number) { data.scrollIntoView item--- index } //自动化测试专用 const setScrollIntoView (id : string) { data.scrollIntoView id } //自动化测试例专用 const check_scroll_height () : Boolean { if (waterflowRef.value null) { console.log(check_scroll_height--waterflowRef is null) return false } var listElement waterflowRef.value as UniWaterFlowElement console.log(check_scroll_height-- listElement.scrollHeight) return listElement.scrollHeight 1400 } const getScrollTop () : number { var ret waterflowRef.value?.scrollTop ?? 0 console.log(ret) return ret } const change_refresher_style_boolean (checked : boolean) { if (checked) { data.refresher_default_style_input none } else { data.refresher_default_style_input black } } const change_load_more_boolean (checked : boolean) { data.hasMore checked } const handleChangeCrossAxisCount (value : number) { if (value 1) { uni.showToast({ title: cross-axis-count 最小值为 1 请重新设置 }) return } data.cross_axis_count value } const handleChangeCrossAxisGap (e : UniSliderChangeEvent) { data.cross_axis_gap e.detail.value } const handleChangeMainAxisGap (e : UniSliderChangeEvent) { data.main_axis_gap e.detail.value } //仅自动化测试调用 const testModifyWaterflowProps () { data.cross_axis_count 4 data.main_axis_gap 6 data.cross_axis_gap 6 data.waterflowPadding [5, 10, 5, 10] } //仅自动化测试调用 const testModifyWaterflowSingleRow () { data.cross_axis_count 1 data.main_axis_gap 6 data.cross_axis_gap 6 } const PAGE_SIZE 5; const NETWORK_DELAY 800; const load_more_click () { if (!data.hasMore) return; // 已无更多 if (data.isLoadingMore) return; // 正在加载中忽略重复点击 data.isLoadingMore true console.log(加载更多开始加载) setTimeout(() { const start data.scrollData.length for (let i 0; i PAGE_SIZE; i) { data.scrollData.push({ height: 90 Math.floor(Math.random() * 120), text: item---${start i} } as flowItemData) } data.isLoadingMore false console.log(加载更多完成当前总数 data.scrollData.length) }, NETWORK_DELAY) // 模拟网络延迟 } defineExpose({ data, confirm_scroll_top_input, change_load_more_boolean, check_scroll_height, setScrollIntoView, getScrollTop, testModifyWaterflowProps, testModifyWaterflowSingleRow }) /script template view classmain waterflow :cross-axis-countdata.cross_axis_count :main-axis-gapdata.main_axis_gap :cross-axis-gapdata.cross_axis_gap :bouncesdata.bounces_boolean :upper-thresholddata.upper_threshold_input :lower-thresholddata.lower_threshold_input :scroll-topdata.scroll_top_input :scroll-leftdata.scroll_left_input :show-scrollbardata.show_scrollbar_boolean :scroll-into-viewdata.scrollIntoView :scroll-with-animationdata.scroll_with_animation_boolean :refresher-enableddata.refresher_enabled_boolean :refresher-backgrounddata.refresher_background_input :refresher-triggereddata.refresher_triggered_boolean :refresher-default-styledata.refresher_default_style_input clickwaterflow_click touchstartwaterflow_touchstart touchmovewaterflow_touchmove touchcancelwaterflow_touchcancel touchendwaterflow_touchend tapwaterflow_tap longpresswaterflow_longpress refresherpullingwaterflow_refresherpulling refresherrefreshwaterflow_refresherrefresh refresherrestorewaterflow_refresherrestore refresherabortwaterflow_refresherabort scrolltoupperwaterflow_scrolltoupper refwaterflowRef scrolltolowerwaterflow_scrolltolower scrollwaterflow_scroll scrollendwaterflow_scrollend stylewidth:100%; idwaterflow :paddingdata.waterflowPadding flow-item v-for(item, index) in data.scrollData :keyindex :iditem.text clickflow_item_click :style{height : item.height} touchstartflow_item_touchstart touchmoveflow_item_touchmove touchcancelflow_item_touchcancel touchendflow_item_touchend tapflow_item_tap longpressflow_item_longpress classflow-item type1 text{{item.text}}/text /flow-item flow-item slotrefresher idrefresher type2 classrefresh-box text classtip-text{{data.text[data.state]}}/text /flow-item flow-item v-showdata.hasMore slotload-more idloadmore type6 classload-more-box clickload_more_click text加载更多/text /flow-item /waterflow /view /template style .main { max-height: 250px; padding: 5px 0; border-bottom: 1px solid rgba(0, 0, 0, .06); flex-direction: row; justify-content: center; } .flow-item { width: 100%; height: 200px; border: 1px solid #666; background-color: #66ccff; align-items: center; justify-content: center; } .tip-text { color: #888; font-size: 12px; } .refresh-box { justify-content: center; align-items: center; flex-direction: row; height: 45px; width: 100%; } .load-more-box { justify-content: center; align-items: center; flex-direction: row; height: 45px; width: 100%; border-style: solid; } /style完整可运行版本见 src/pages/component/waterflow/waterflow.uvue。该示例中值得注意的实战细节列数校验handleChangeCrossAxisCount中对小于 1 的列数提示cross-axis-count 最小值为 1下拉刷新状态机通过data.state0继续下拉执行刷新、1释放立即刷新、2刷新中、3空态配合refresherpulling中的e.detail.dy 45判断驱动自定义刷新元素文案加载更多防抖isLoadingMore标志位避免重复触发加载hasMore控制 load-more 插槽的显示/隐藏事件类型断言通过自定义ScrollEventTest类型对e.target/e.currentTarget做instanceof UniElement断言用于自动化测试验证事件参数结构。子组件一览| 子组件 | 说明 | | :- | :- | | flow-item | waterflow 的唯一合法子组件Android 4.41、iOS 4.41、HarmonyOS(VDOM) 4.81、HarmonyOS(Vapor) 5.02 |参见flow-item 组件文档list-view 组件文档复用机制与性能优化scroll-view 组件文档自定义下拉刷新UTS 数据类型与 string.ColorString / string.IDString组件源码与实现waterflow.uvue、flow-item.uvue、recycle_waterflow.cpp示例与测试waterflow.uvue 示例页、waterflow-fit-height.uvue、waterflow.test.js、waterflow-perf.uvue、long-waterflow-nested.uvue【免费下载链接】uni-appA cross-platform framework using Vue.js项目地址: https://gitcode.com/gh_mirrors/un/uni-app创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED

相关推荐

PaddleOCR JS 浏览器端 OCR SDK 开发指南:monorepo 构建流程、TypeScript 工程化与 npm 分发兼容实践

PaddleOCR JS 浏览器端 OCR SDK 开发指南:monorepo 构建流程、TypeScript 工程化与 npm 分发兼容实践

PaddleOCR JS 浏览器端 OCR SDK 开发指南:monorepo 构建流程、TypeScript 工程化与 npm 分发兼容实践 【免费下载链接】PaddleOCR 飞桨多语言OCR工具包(实用超轻量OCR系统,支持80种语言识别,提供数据标注与合成工具,支…

📅 2026/9/19 23:53:57
Slang 泛型与存在类型特化:基于 `coverage/specialize` 测试束的 IR 特化行为剖析

Slang 泛型与存在类型特化:基于 `coverage/specialize` 测试束的 IR 特化行为剖析

Slang 泛型与存在类型特化:基于 coverage/specialize 测试束的 IR 特化行为剖析 【免费下载链接】slang Making it easier to work with shaders 项目地址: https://gitcode.com/GitHub_Trending/sl/slang 本文以 docs/generated/tests/coverage/specialize/…

📅 2026/9/19 23:53:57
uni-app 的 uni_modules 插件规范:从目录结构、package.json 配置到插件开发与发布全指南

uni-app 的 uni_modules 插件规范:从目录结构、package.json 配置到插件开发与发布全指南

uni-app 的 uni_modules 插件规范:从目录结构、package.json 配置到插件开发与发布全指南 【免费下载链接】uni-app A cross-platform framework using Vue.js 项目地址: https://gitcode.com/gh_mirrors/un/uni-app 导读 uni_modules 是 uni-app 官方提供的…

📅 2026/9/19 23:53:57
MORE NEWS

更多资讯

📰

考博英语命题逻辑与量化备考:从题型边界到主观题评分矩阵

简介:PDF文档《博士生入学考试英语备考指南》由清华大学出版社出版,面向报考博士研究生的考生,系统梳理了博士生英语入学考试的核心题型、难度层次与备考要点,既适合首次备考者建立整体认知,也适合考前冲刺阶段查漏补缺…

📰

Cursor 是编程提效工具,Base URL 走 TaoToken 通道行不行

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

CC Switch 里 TaoToken 当默认供应商:Qwen3.7 Plus 一键切换

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

Windows 11预览功能彻底关闭指南:三层机制深度解析

1. 这个“预览”到底在预览什么?先搞清它的真实身份 很多人一看到文件管理器里点个PDF、点个图片就弹出右侧那个半透明窗格,第一反应就是“哦,这是Windows 11新加的‘预览窗格’”,然后直奔设置里去关——结果发现关了又开&#…

📰

自考04747《Java语言程序设计(一)》考点清单:从基础语法到经典算法题

简介:面向高等教育自学考试考生整理的《Java语言程序设计(一)》(课程代码04747)复习资料PDF,聚焦Java语言基础,可作为自考课程配套辅导与考前冲刺用书。资源为单个PDF文件,大小1.23M…

📰

Hasura GraphQL Engine 元数据乐观并发控制(Optimistic Concurrency Control)设计解析

后端API网关数据库GraphQL 【免费下载链接】graphql-engine Blazing fast, instant realtime GraphQL APIs on all your data with fine grained access control, also trigger webhooks on database events. 项目地址: https://gitcode.com/gh_mirrors/gr/graphql-…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬