更新记录
1.2.0(2025-12-12)
Android 兼容性优化
- 修复布局计算:修复 Grid 布局模式下
textExtraHeight为 0 时的计算逻辑问题 - 优化宽度测量:优化 Android 平台下的容器宽度获取方式,提高兼容性
- 文档更新:更新 readme,增加 Android 平台网格布局的最佳实践说明
1.1.1(2025-12-01)
重大更新
- UTS 重构:核心布局引擎使用 UTS (Uni Type Script) 完全重写,在 uni-app x 中提供原生级性能
- uni-app x 支持:全面支持 uni-app x (Android/iOS),提供专属
.uvue组件 - 双引擎架构:同时保留 JS 引擎和 UTS 引擎,完美兼容普通 uni-app 项目和 uni-app x 项目
优化与修复
- Android 兼容性:修复 UTSJSONObject 类型访问问题,优化 DOM 测量逻辑
- iOS 兼容性:修复 CSS 样式兼容性问题(移除不支持的 display: block 等)
- H5 兼容性:修复模板闭合标签和容器样式问题
- 布局优化:
- 优化动态卡片高度预估算法,减少视觉空白
- 修复瀑布流垂直间距计算逻辑
- 优化 Flexbox 布局,确保多端视觉一致性
1.1.0(2025-12-01)
查看更多平台兼容性
uni-app(3.6.15)
| Vue2 | Vue2插件版本 | Vue3 | Vue2插件版本 | Chrome | Chrome插件版本 | Safari | Safari插件版本 | app-vue | app-vue插件版本 | app-nvue | Android | Android插件版本 | iOS | iOS插件版本 | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| √ | 1.2.0 | √ | 1.2.0 | √ | 1.2.0 | √ | 1.2.0 | √ | 1.2.0 | - | 5.0 | 1.2.0 | 12 | 1.2.0 | - |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|
| - | - | - | - | - | - | - | - | - | - | - |
uni-app x(3.6.15)
| Chrome | Chrome插件版本 | Safari | Safari插件版本 | Android | Android插件版本 | iOS | iOS插件版本 | 鸿蒙 | 微信小程序 |
|---|---|---|---|---|---|---|---|---|---|
| √ | 1.2.0 | √ | 1.2.0 | 5.0 | 1.2.0 | 12 | 1.2.0 | - | - |
其他
| 多语言 | 暗黑模式 | 宽屏模式 |
|---|---|---|
| √ | √ | √ |
hy-virtual-waterfall
高性能虚拟列表与瀑布流组件
专为 uni-app 打造的虚拟渲染瀑布流组件,支持海量数据流畅滚动
✨ 特性
- 🚀 极致性能 - 虚拟渲染技术,支持10000+数据流畅滚动
- ⚡ UTS原生 - 基于 UTS 重构核心逻辑,在 uni-app x 中提供原生级性能
- 🎨 多种布局 - 支持瀑布流、网格、均匀等多种布局模式
- 📐 智能计算 - 自动计算最优布局,自适应容器宽度
- 🔄 动态尺寸 - 支持内容高度自动测量和更新
- 📱 多端支持 - 完美支持 H5、小程序、App(vue/nvue/uvue)
- 🎯 易用性 - 简洁的 API 设计,丰富的配置选项
- 📝 TypeScript - 完整的类型定义支持
📢 uni-app x 支持说明
本插件已全面适配 uni-app x,提供原生级的渲染性能。
- 组件路径:
uni_modules/hy-virtual-waterfall/components/hy-virtual-waterfall/hy-virtual-waterfall.uvue - 核心逻辑:使用 UTS (Uni Type Script) 重写,运行在原生层
- 类型安全:提供完整的 UTS 类型定义 (
LayoutConfig,VirtualConfig)
⚠️ Android 平台注意事项
在 uni-app x (Android) 中,由于严格的类型限制,访问 UTSJSONObject 属性时必须使用方括号语法:
// ❌ 错误写法 (Android 编译报错)
<text>{{ item.title }}</text>
// ✅ 正确写法
<text>{{ item['title'] }}</text>
⚠️ Android 网格布局注意事项
在 Android 平台使用 网格布局 (Grid) 时,为确保正确渲染,请务必保证数据项的 aspectRatio 为 1 (正方形),否则可能会显示为瀑布流错乱样式:
// Android 网格模式数据生成示例
{
aspectRatio: 1.0, // 🔴 必须显式设置为 1.0
image: '...',
title: '...'
}
🎯 快速开始
<template>
<view class="container">
<hy-virtual-waterfall
:data="feedList"
:layout-config="layoutConfig"
:virtual-config="virtualConfig"
:loading="loading"
:has-more="hasMore"
@load-more="loadMoreData"
@item-click="onItemClick"
@item-visible="onItemVisible"
>
<template #item="{ item, index }">
<view class="feed-item">
<image :src="item['cover']" mode="widthFix" class="feed-cover" />
<view class="feed-content">
<text class="feed-title">{{ item['title'] }}</text>
<text class="feed-desc">{{ item['description'] }}</text>
<view class="feed-meta">
<text class="feed-author">{{ item['author'] }}</text>
<text class="feed-time">{{ item['time'] }}</text>
</view>
</view>
</view>
</template>
</hy-virtual-waterfall>
</view>
</template>
<script>
export default {
data() {
return {
feedList: [],
loading: false,
hasMore: true,
layoutConfig: {
type: 'waterfall',
columns: 2,
columnGap: 12,
rowGap: 12
},
virtualConfig: {
enabled: true,
overscan: 3,
estimatedHeight: 300,
useDynamicHeight: true
}
};
},
mounted() {
this.loadInitialData();
},
methods: {
async loadInitialData() {
this.loading = true;
try {
const data = await this.fetchFeedData(0);
this.feedList = data;
} catch (error) {
console.error('加载数据失败:', error);
} finally {
this.loading = false;
}
},
async loadMoreData() {
if (this.loading || !this.hasMore) return;
this.loading = true;
try {
const newData = await this.fetchFeedData(this.feedList.length);
this.feedList = [...this.feedList, ...newData];
this.hasMore = newData.length > 0;
} catch (error) {
console.error('加载更多失败:', error);
} finally {
this.loading = false;
}
},
onItemClick(item, index) {
console.log('点击项目:', item, index);
// uni-app x 兼容写法
uni.navigateTo({
url: `/pages/detail?id=${item['id']}`
});
},
onItemVisible(item, index) {
// 记录曝光数据
this.trackExposure(item['id']);
}
}
};
</script>
<style scoped>
.container {
height: 100vh;
}
.feed-item {
background: #fff;
border-radius: 12rpx;
overflow: hidden;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08);
}
.feed-cover {
width: 100%;
display: block;
}
.feed-content {
padding: 24rpx;
}
.feed-title {
display: block;
font-size: 28rpx;
font-weight: bold;
color: #333;
margin-bottom: 12rpx;
line-height: 1.4;
}
.feed-desc {
display: block;
font-size: 24rpx;
color: #666;
line-height: 1.4;
margin-bottom: 16rpx;
}
.feed-meta {
display: flex;
justify-content: space-between;
align-items: center;
}
.feed-author {
font-size: 22rpx;
color: #999;
}
.feed-time {
font-size: 22rpx;
color: #999;
}
</style>
引入组件
uni-app x 项目 (.uvue):
import HyVirtualWaterfall from '@/uni_modules/hy-virtual-waterfall/components/hy-virtual-waterfall/hy-virtual-waterfall.uvue';
import type { LayoutConfig, VirtualConfig } from '@/uni_modules/hy-virtual-waterfall/js_sdk/uts/types.uts';
export default {
components: {
HyVirtualWaterfall
}
}
普通 uni-app 项目 (.vue):
import HyVirtualWaterfall from '@/uni_modules/hy-virtual-waterfall/components/hy-virtual-waterfall/hy-virtual-waterfall.vue';
export default {
components: {
HyVirtualWaterfall
}
}
📖 基础示例
社交动态瀑布流
<template>
<hy-virtual-waterfall
:data="socialFeeds"
:layout-config="{
type: 'waterfall',
columns: 2,
columnGap: 16,
rowGap: 16
}"
@load-more="loadMoreFeeds"
>
<template #item="{ item }">
<view class="social-card">
<image :src="item['user']['avatar']" class="user-avatar" />
<text class="user-name">{{ item['user']['name'] }}</text>
<image
v-for="(img, idx) in item['images']"
:key="idx"
:src="img"
mode="widthFix"
class="feed-image"
/>
<text class="feed-text">{{ item['content'] }}</text>
</view>
</template>
</hy-virtual-waterfall>
</template>
商品网格布局
<template>
<hy-virtual-waterfall
:data="products"
:layout-config="{
type: 'grid',
columns: 3,
columnGap: 8,
rowGap: 8
}"
@item-click="onProductClick"
>
<template #item="{ item }">
<view class="product-card">
<image :src="item['image']" mode="aspectFill" class="product-image" />
<text class="product-name">{{ item['name'] }}</text>
<text class="product-price">¥{{ item['price'] }}</text>
</view>
</template>
</hy-virtual-waterfall>
</template>
📚 API 文档
Props 属性
| 属性名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| data | Array | [] | 数据源 |
| layoutConfig | Object | {...} | 布局配置 |
| virtualConfig | Object | {...} | 虚拟配置 |
| loading | Boolean | false | 加载状态 |
| hasMore | Boolean | true | 是否有更多数据 |
| emptyText | String | '暂无数据' | 空状态文本 |
| showBackTop | Boolean | true | 是否显示回到顶部 |
| height | String | '100vh' | 容器高度 |
Events 事件
| 事件名 | 参数 | 说明 |
|---|---|---|
| load-more | - | 加载更多数据 |
| item-click | (item, index) | 项目点击事件 |
| item-visible | (item, index) | 项目可见事件 |
| scroll | (scrollTop) | 滚动事件 |
| scroll-to-bottom | - | 滚动到底部 |
| scroll-to-top | - | 滚动到顶部 |
layoutConfig 布局配置
| 属性名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| type | String | 'waterfall' | 布局类型:'waterfall' 瀑布流 / 'grid' 网格 |
| columns | Number | 2 | 列数 |
| columnGap | Number | 12 | 列间距(px) |
| rowGap | Number | 12 | 行间距(px) |
| minColumnWidth | Number | 100 | 最小列宽(px),用于响应式布局 |
| maxColumnWidth | Number | 400 | 最大列宽(px),用于响应式布局 |
| textExtraHeight | Number | - | 文字内容额外高度(px),用于预估项目高度。瀑布流建议120,网格布局建议0 |
textExtraHeight 说明:
- 瀑布流布局:如果内容包含标题、描述等文字,建议设置为
120,为文字内容预留空间 - 网格布局:如果文字是 overlay 覆盖在图片上,设置为
0 - 不设置:自动根据 item.data 是否有 title/description/author 判断(有则100,无则0)
// 瀑布流配置示例
layoutConfig: {
type: 'waterfall',
columns: 2,
columnGap: 12,
rowGap: 12,
minColumnWidth: 120,
maxColumnWidth: 400,
textExtraHeight: 120 // 为文字内容预留空间
}
// 网格配置示例
layoutConfig: {
type: 'grid',
columns: 3,
columnGap: 8,
rowGap: 8,
minColumnWidth: 80,
maxColumnWidth: 200,
textExtraHeight: 0 // 文字是overlay,不需要额外高度
}
virtualConfig 虚拟配置
| 属性名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| enabled | Boolean | true | 是否启用虚拟滚动 |
| overscan | Number | 3 | 预渲染倍数 |
| estimatedHeight | Number | 300 | 预估项目高度(px) |
布局算法说明
本组件参考 iOS UICollectionView 的瀑布流布局算法,具有以下特点:
- 布局缓存:位置一旦计算完成就不再改变,避免滚动时布局跳动
- 精确预估:基于
aspectRatio一次性计算准确高度,不依赖后续测量 - 列高度持久化:上拉加载新数据时自动继承当前列高度,无缝衔接
- 减少测量:只在首次加载前10个item时允许微调,后续item布局锁定
最佳实践:
// ✅ 推荐:提供准确的 aspectRatio
const feedList = [
{
id: 1,
image: 'https://example.com/image.jpg', // 400x600
aspectRatio: 600 / 400, // 1.5 (高度/宽度)
title: '标题',
description: '描述'
}
];
// ❌ 不推荐:aspectRatio 不准确会导致布局偏差
const feedList = [
{
id: 1,
image: 'https://example.com/image.jpg',
aspectRatio: 1, // 实际图片是1.5,会导致计算错误
}
];
为什么需要准确的 aspectRatio?
- iOS 方式依赖预估高度一次性布局,完全不会在运行时动态调整
- 这样可以避免滚动时布局跳动,消除累积误差
- 如果 aspectRatio 不准确,会导致预留空间不足或过多
如何调整间距?
如果发现间距不合适,请调整配置参数:
// 1. 调整 textExtraHeight(文字内容高度)
basicLayoutConfig: {
textExtraHeight: 140 // 默认140px,根据实际内容调整
}
// 2. 调整 rowGap(行间距)
basicLayoutConfig: {
rowGap: 12 // 增大或减小间距
}
// 3. 确保 aspectRatio 准确
// 图片 400x600 → aspectRatio = 600/400 = 1.5
// 图片 400x500 → aspectRatio = 500/400 = 1.25
不要依赖动态测量! 本组件已完全禁用运行时高度更新,以避免累积误差。
Methods 方法
通过 ref 调用组件方法:
// 滚动到指定项目
this.$refs.waterfall.scrollToItem(itemId, 100);
// 添加数据
this.$refs.waterfall.appendData(newData);
// 重新计算布局
this.$refs.waterfall.recalculateLayout();
// 回到顶部
this.$refs.waterfall.scrollToTop();
🚀 高级功能示例
动态列数适配
// 根据屏幕宽度动态计算列数
computed: {
responsiveLayoutConfig() {
const screenWidth = uni.getSystemInfoSync().screenWidth;
const columns = screenWidth > 750 ? 3 : 2;
return {
columns,
columnGap: 12,
rowGap: 12,
maxColumnWidth: 280,
minColumnWidth: 200
};
}
}
图片预加载优化
// 在项目可见时预加载图片
onItemVisible(item, index) {
if (item.images && item.images.length > 0) {
item.images.forEach(imgUrl => {
const img = new Image();
img.src = imgUrl; // 触发预加载
});
}
}
🔧 配置项详解
LayoutConfig (布局配置)
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| type | String | 'waterfall' | 布局类型:waterfall | grid | uniform |
| columns | Number | 2 | 列数 |
| columnGap | Number | 12 | 列间距(px) |
| rowGap | Number | 12 | 行间距(px) |
| maxColumnWidth | Number | - | 最大列宽(px) |
| minColumnWidth | Number | - | 最小列宽(px) |
VirtualConfig (虚拟配置)
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| enabled | Boolean | true | 是否启用虚拟渲染 |
| overscan | Number | 5 | 预渲染屏幕外的项目数量 |
| estimatedHeight | Number | 200 | 预估项目高度(px) |
| useDynamicHeight | Boolean | true | 是否使用动态高度 |
🎨 使用场景
- 📸 图片瀑布流展示(如 Pinterest、小红书)
- 🛍️ 商品列表展示(如淘宝、京东)
- 📰 新闻资讯流(如今日头条)
- 🎬 视频列表展示(如抖音、B站)
- 📱 社交动态流(如微博、朋友圈)
- 🎨 作品集展示(如Dribbble、Behance)
⚡ 性能优化
- 虚拟渲染 - 只渲染可见区域,大幅减少 DOM 节点
- GPU 加速 - 使用 transform3d 开启硬件加速
- 智能缓存 - 缓存布局和尺寸信息,避免重复计算
- 批量处理 - 批量更新操作,减少重绘次数
- 防抖节流 - 滚动事件防抖处理
如果这个组件对你有帮助,请给个⭐️支持一下!

收藏人数:
购买源码授权版(
试用
使用 HBuilderX 导入示例项目
赞赏(2)
下载 79
赞赏 2
下载 12197496
赞赏 1828
赞赏
京公网安备:11010802035340号