更新记录
1.0.1(2025-10-30) 下载此版本
修改示例说明文件
1.0.0(2025-10-30) 下载此版本
平台兼容性
uni-app(3.6.12)
| Vue2 | Vue2插件版本 | Vue3 | Vue2插件版本 | Chrome | Chrome插件版本 | Safari | Safari插件版本 | app-vue | app-vue插件版本 | app-nvue | app-nvue插件版本 | Android | Android插件版本 | iOS | iOS插件版本 | 鸿蒙 | 鸿蒙插件版本 | 
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| √ | 1.0.0 | √ | 1.0.0 | √ | 1.0.0 | √ | 1.0.0 | √ | 1.0.0 | √ | 1.0.0 | 5.0 | 1.0.0 | 12 | 1.0.0 | √ | 1.0.0 | 
| 微信小程序 | 微信小程序插件版本 | 支付宝小程序 | 支付宝小程序插件版本 | 抖音小程序 | 抖音小程序插件版本 | 百度小程序 | 百度小程序插件版本 | 快手小程序 | 快手小程序插件版本 | 京东小程序 | 京东小程序插件版本 | 鸿蒙元服务 | 鸿蒙元服务插件版本 | QQ小程序 | QQ小程序插件版本 | 飞书小程序 | 飞书小程序插件版本 | 快应用-华为 | 快应用-华为插件版本 | 快应用-联盟 | 快应用-联盟插件版本 | 
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| √ | 1.0.0 | √ | 1.0.0 | √ | 1.0.0 | √ | 1.0.0 | √ | 1.0.0 | √ | 1.0.0 | √ | 1.0.0 | √ | 1.0.0 | √ | 1.0.0 | √ | 1.0.0 | √ | 1.0.0 | 
uni-app x(3.6.12)
| Chrome | Chrome插件版本 | Safari | Safari插件版本 | Android | Android插件版本 | iOS | iOS插件版本 | 鸿蒙 | 鸿蒙插件版本 | 微信小程序 | 微信小程序插件版本 | 
|---|---|---|---|---|---|---|---|---|---|---|---|
| √ | 1.0.0 | √ | 1.0.0 | 5.0 | 1.0.0 | 12 | 1.0.0 | √ | 1.0.0 | √ | 1.0.0 | 
其他
| 多语言 | 暗黑模式 | 宽屏模式 | 
|---|---|---|
| √ | √ | √ | 
hy-virtual-waterfall
高性能虚拟列表与瀑布流组件
专为 uni-app 打造的虚拟渲染瀑布流组件,支持海量数据流畅滚动
✨ 特性
- 🚀 极致性能 - 虚拟渲染技术,支持10000+数据流畅滚动
- 🎨 多种布局 - 支持瀑布流、网格、均匀等多种布局模式
- 📐 智能计算 - 自动计算最优布局,自适应容器宽度
- 🔄 动态尺寸 - 支持内容高度自动测量和更新
- ⚡ 高性能 - GPU加速、缓存优化、批量处理
- 📱 多端支持 - 完美支持 H5、小程序、App(vue/nvue)
- 🎯 易用性 - 简洁的 API 设计,丰富的配置选项
- 📝 TypeScript - 完整的类型定义支持
🎯 快速开始
<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.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>手动引入组件
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
                                                        下载插件并导入HBuilderX
                                                     赞赏(0)
                                        赞赏(0)
                                     
                                             
                                             下载 26
 下载 26
                 赞赏 0
 赞赏 0
                 
             
                     下载 10661747
                    下载 10661747 
                 赞赏 1797
                        赞赏 1797 
                     
             
                     
             
                     
             
                     
             
                     
             
                     
             
                     
             
                     
             
                     
             
                     
             
                     
             
                     
             
                     
             
                     
             
                     
                         赞赏
                                赞赏
                             
             京公网安备:11010802035340号
京公网安备:11010802035340号