
核心思路1、新增数据只向最矮列追加已有卡片位置不重排数据更新时采用增量分配策略将新卡片插入当前卡片数量最少即最矮的列避免触发整个列表的重排极大提升性能。2、宽度flex自适应高度由内容撑开组件实现以下是完整的瀑布流组件代码包含模板、逻辑与样式。template view classwf view v-for(col, index) in cols :keyindex classwf__col :style{ marginLeft: index 0 ? 0 : gap rpx } view v-foritem in col :keyitem.id classwf__card slot namecard :itemitem !-- 默认卡片图片 文案 -- image classwf__img :srcitem.imgUrl modewidthFix lazy-load / view classwf__text{{ item.text }}/view /slot /view /view /view /template script setup import { ref, watch } from vue const props defineProps({ list: { type: Array, default: () [] }, column: { type: Number, default: 2 }, gap: { type: Number, default: 0 }, }) // 初始即按列数初始化 const cols ref(Array.from({ length: props.column }, () [])) let done 0 // 已处理的卡片数 /** * 增量分配逐个放入当前卡片数最少的列 * 列自然高度差由浏览器 flex 布局处理这里只需保证分配均衡 */ const append (items) { for (const raw of items) { let minIdx 0 for (let i 1; i props.column; i) { if (cols.value[i].length cols.value[minIdx].length) minIdx i } cols.value[minIdx].push(raw) } } watch( () props.list, (list) { const added list.slice(done) if (!added.length) return append(added) done list.length console.log(list变化了, done, list) }, { immediate: true } ) /script style scoped .wf { display: flex; align-items: flex-start; width: 100%; } .wf__col { flex: 1; display: flex; flex-direction: column; } .wf__card { margin-bottom: var(--wf-gap, 12px); border-radius: 12rpx; overflow: hidden; background: #fff; box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05); break-inside: avoid; } .wf__img { width: 100%; display: block; background: #f2f3f5; } .wf__text { padding: 12rpx 16rpx; font-size: 26rpx; color: #333; line-height: 1.4; word-break: break-all; } /style关键逻辑解析1. 数据结构与初始化组件内部维护一个响应式数组cols长度为列数props.column每个元素是一个卡片数组代表一列的数据。done变量记录已处理的数据量用于增量计算。2. 增量分配算法append函数是核心分配逻辑。遍历待添加的数据项每次都将当前项放入cols中长度最短卡片最少的列。这保证了新增数据总是流向最矮的列使各列高度尽可能均衡同时避免了已有卡片的位置变动。3. 响应式监听与性能优化通过watch监听props.list的变化计算新增的数据片段list.slice(done)只对新增部分执行append操作。最后更新done计数器。这种设计确保了即使列表频繁更新如分页加载、筛选也只有新增的卡片会触发分配和渲染已有卡片保持原位性能极佳。使用示例template scroll-view classscroll-wrap scroll-y :lower-threshold100 scrolltoloweronReachBottom view classcontent view classgoods_list v-ifgoodsList.length WaterfallFlow :listgoodsList :column2 :gap30 template #card{ item } image classcard-img :srcitem.imgUrl modewidthFix lazy-load / view classcard-text{{ item.text }}/view /template /WaterfallFlow /view !-- 底部加载状态 -- view classload-status text v-ifloading加载中.../text text v-else-ifnoMore没有更多了/text text v-else上拉加载更多/text /view /view /scroll-view /template script setup import WaterfallFlow from ./WaterfallFlow.vue import { ref, onMounted } from vue const goodsList ref([]) const loading ref(false) const noMore ref(false) // 模拟最大加载次数到达后不再加载 const MAX_PAGE 5 // 首屏视为第 1 页所以从 0 开始loadMore 后 currentPage 1 let currentPage 0 // 图片素材池模拟接口返回的随机图片 const IMAGE_POOL [ https://assets.699pic.com/public/web/images/300/001/058.jpg!list1x.v2, https://assets.699pic.com/public/web/images/306/227/884.jpg!list1x.v2, https://assets.699pic.com/public/web/images/300/039/938.jpg!list1x.v2, https://assets.699pic.com/public/web/images/303/730/623.jpg!list1x.v2, ] // 文案素材池模拟不同长度的标题 const TEXT_POOL [ 新品上市限时折扣, 爆款热卖中赶紧下单吧爆款热卖中赶紧下单吧爆款热卖中赶紧下单吧, 精选好物品质保证, 限时秒杀手慢无, 夏季清仓特惠全场包邮, 种草好物推荐, 高品质生活从这开始, 设计师款限量发售, 好评如潮回购率高好评如潮回购率高好评如潮回购率高好评如潮回购率高, 节日特惠买二送一, 舒适透气亲肤面料舒适透气亲肤面料舒适透气亲肤面料舒适透气亲肤面料舒适透气亲肤面料舒适透气亲肤面料, 潮流百搭时尚单品, ] /** * 生成指定数量的模拟数据 * param {number} startId 起始 id * param {number} count 生成数量 */ const generateMockData (startId, count 10) { return Array.from({ length: count }, (_, i) ({ id: startId i, imgUrl: IMAGE_POOL[Math.floor(Math.random() * IMAGE_POOL.length)], text: TEXT_POOL[Math.floor(Math.random() * TEXT_POOL.length)], })) } /** * 模拟异步请求加载更多数据 */ const loadMore async () { if (loading.value || noMore.value) return loading.value true // 模拟接口请求延迟 await new Promise((resolve) setTimeout(resolve, 600)) const nextStartId goodsList.value.length ? goodsList.value[goodsList.value.length - 1].id 1 : 1 const newData generateMockData(nextStartId, 10) // 追加数据WaterfallFlow 会通过 watch 增量分配到各列 goodsList.value [...goodsList.value, ...newData] currentPage 1 if (currentPage MAX_PAGE) { noMore.value true } loading.value false } /** * scroll-view 滚动到底部触发 */ const onReachBottom () { loadMore() } // 首屏自动加载第一页数据 onMounted(() { loadMore() }) /script style langscss .scroll-wrap { /* scroll-view 必须有明确高度才能滚动 */ height: 100vh; box-sizing: border-box; } .content { display: flex; flex-direction: column; margin: 10rpx; .goods_list { margin: 0 26rpx; } .card-img { width: 100%; display: block; background: #f2f3f5; } .card-text { padding: 6rpx 12rpx; font-size: 26rpx; color: #333; line-height: 1.45; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; line-clamp: 3; overflow: hidden; } .empty { text-align: center; padding: 40rpx; color: #999; font-size: 26rpx; } .load-status { text-align: center; padding: 30rpx 0 60rpx; color: #999; font-size: 24rpx; } } /style总结本瀑布流组件通过“内容撑开高度 图片宽度自适应 增量追加至最矮列”的组合策略实现了视觉上自然的错落效果与优秀的渲染性能。同时通过插槽机制提供了高度的自定义能力可以轻松适配各种业务场景。