news 2026/3/8 14:20:35

vue2 瀑布流 组件

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
vue2 瀑布流 组件

该源码来自 uviewpro 地址为:https://uviewpro.cn/zh/components/waterfall.html

我改成vue2的写法 优化了计时器

瀑布流插件

<template><divclass="waterfull"><divclass="left"ref="leftRef"><slot name="left":leftList="leftData"></slot></div><divclass="right"ref="rightRef"><slot name="right":rightList="rightData"></slot></div></div></template><script>/** * * @author COOBY * @since 2026-01-26 14:19 */exportdefault{props:{list:{type:Array,default:()=>[]},addTime:{type:Number,default:200}},data(){return{timer:null,leftData:[],rightData:[],tempList:[],}},computed:{copyFlowList(){returnthis.deepClone(this.list);}},watch:{copyFlowList(nVal,oVal){conststartIndex=Array.isArray(oVal)&&oVal.length>0?oVal.length:0;// 拼接上原有数据this.tempList=this.tempList.concat(this.deepClone(nVal.slice(startIndex)));this.splitData();},immediate:true},mounted(){this.tempList=this.deepClone(this.copyFlowList);this.splitData();},beforeDestroy(){if(this.timer)clearTimeout(this.timer);this.timer=null;},methods:{asyncsplitData(){if(!this.tempList.length)return;if(!this.$refs.leftRef||!this.$refs.rightRef)return;awaitthis.$nextTick();constleftRect=this.$refs.leftRef.offsetHeight;constrightRect=this.$refs.rightRef.offsetHeight;// 如果左边小于或等于右边,就添加到左边,否则添加到右边constitem=this.tempList[0];// 解决多次快速上拉后,可能数据会乱的问题,因为经过上面的两个await节点查询阻塞一定时间,加上后面的定时器干扰// 数组可能变成[],导致此item值可能为undefinedif(!item)return;if(leftRect<rightRect){this.leftData.push(item);}elseif(leftRect>rightRect){this.rightData.push(item);}else{// 这里是为了保证第一和第二张添加时,左右都能有内容// 因为添加第一张,实际队列的高度可能还是0,这时需要根据队列元素长度判断下一个该放哪边if(this.leftData.length<=this.rightData.length){this.leftData.push(item);}else{this.rightData.push(item);}}// 移除临时列表的第一项this.tempList.shift();// 如果临时数组还有数据,继续循环awaitthis.$nextTick();if(this.tempList.length){if(this.timer)clearTimeout(this.timer);this.timer=setTimeout(()=>{this.splitData();},Math.max(0,this.addTime));// 防止负数}},deepClone(obj,cache=newWeakMap()){if(obj===null||typeofobj!=='object')returnobj;if(cache.has(obj))returncache.get(obj);letclone;if(objinstanceofDate){clone=newDate(obj.getTime());}elseif(objinstanceofRegExp){clone=newRegExp(obj);}elseif(objinstanceofMap){clone=newMap(Array.from(obj,([key,value])=>[key,this.deepClone(value,cache)]));}elseif(objinstanceofSet){clone=newSet(Array.from(obj,value=>this.deepClone(value,cache)));}elseif(Array.isArray(obj)){clone=obj.map(value=>this.deepClone(value,cache));}elseif(Object.prototype.toString.call(obj)==='[object Object]'){clone=Object.create(Object.getPrototypeOf(obj));cache.set(obj,clone);for(const[key,value]ofObject.entries(obj)){clone[key]=this.deepClone(value,cache);}}else{clone=Object.assign({},obj);}cache.set(obj,clone);returnclone;}},}</script><style lang="less"scoped>.waterfull{display:flex;gap:2vw;width:92vw;margin:0auto;.left,.right{flex:1;background-color:#fff;height:fit-content;}}</style>

vue中使用

<waterfull:list="videoList"><template slot="left"slot-scope="{ leftList }"><div v-for="(item, index) in leftList":key="index"><itemVideo:item="item"></itemVideo></div></template><template slot="right"slot-scope="{ rightList }"><div v-for="(item, index) in rightList":key="index"><itemVideo:item="item"></itemVideo></div></template></waterfull>

一、需求目标

我们要实现一个组件,具备以下能力:

  • 接收一个动态变化的list数组(比如通过上拉加载新增数据);
  • 自动将新项“智能”分配到左列或右列,使两列高度尽可能平衡;
  • 支持自定义每项添加的间隔时间(模拟“逐个加载”的动画效果);
  • 避免因频繁更新导致的数据错乱或性能问题。

二、整体结构概览

<template> <div class="waterfull"> <div class="left" ref="leftRef"> <slot name="left" :leftList="leftData"></slot> </div> <div class="right" ref="rightRef"> <slot name="right" :rightList="rightData"></slot> </div> </div> </template>
  • 使用<slot>实现插槽分发,父组件可自由定义左右列的渲染方式;
  • 通过ref获取左右容器的真实 DOM 高度,用于判断插入位置;
  • 数据分为leftDatarightData两个数组,分别控制左右列内容。

三、核心逻辑拆解

1. 数据监听与增量处理

watch:{copyFlowList(nVal,oVal){conststartIndex=Array.isArray(oVal)&&oVal.length>0?oVal.length:0;this.tempList=this.tempList.concat(this.deepClone(nVal.slice(startIndex)));this.splitData();},immediate:true}
  • copyFlowList是对props.list的深拷贝(避免直接修改原始数据);
  • list变化时,只取新增部分slice(startIndex)),避免重复处理已有项;
  • 新增项先存入tempList临时队列,再交由splitData逐步分配。

为什么用临时队列?
因为我们希望“逐个”添加项(带时间间隔),而不是一次性塞入,这样能模拟真实加载过程,并防止 DOM 高度计算不准。


2. 智能分配算法:splitData

这是整个组件的灵魂函数

asyncsplitData(){if(!this.tempList.length)return;if(!this.$refs.leftRef||!this.$refs.rightRef)return;awaitthis.$nextTick();// 确保 DOM 已更新constleftRect=this.$refs.leftRef.offsetHeight;constrightRect=this.$refs.rightRef.offsetHeight;constitem=this.tempList[0];if(!item)return;if(leftRect<=rightRect){this.leftData.push(item);}else{this.rightData.push(item);}this.tempList.shift();// 移除已处理项awaitthis.$nextTick();// 等待新项渲染完成,高度更新if(this.tempList.length){if(this.timer)clearTimeout(this.timer);this.timer=setTimeout(()=>{this.splitData();},Math.max(0,this.addTime));}}
分配策略详解:
条件行为
leftHeight <= rightHeight新项放入左边
leftHeight > rightHeight新项放入右边

💡为什么不是严格<而是<=
这样能确保第一项优先放入左边,第二项若高度相同(都为0),则进入else分支中的兜底逻辑。

兜底逻辑(初始状态处理):
// 当左右高度相等(如初始都为0)时if(this.leftData.length<=this.rightData.length){this.leftData.push(item);}else{this.rightData.push(item);}
  • 防止前两项都塞到同一侧;
  • 保证左右列都能有内容,提升首屏体验。

3. 异步调度与防抖

  • 每次添加一项后,等待 DOM 渲染完成$nextTick())再计算下一次高度;
  • 使用setTimeout控制添加频率(addTime默认 200ms);
  • 每次调用前clearTimeout,防止多个定时器堆积(尤其在快速上拉加载时)。

⚠️注意:如果不加$nextTick()offsetHeight可能还是旧值,导致分配错误!


4. 深拷贝工具函数

deepClone(obj,cache=newWeakMap()){// 处理 null、基本类型、Date、RegExp、Map、Set、Array、Object 等// 使用 WeakMap 防止循环引用}
  • 避免外部传入的对象被组件内部修改;
  • 支持复杂嵌套结构,适用于大多数业务场景。

四、样式与布局

.waterfull { display: flex; gap: 2vw; width: 92vw; margin: 0 auto; .left, .right { flex: 1; background-color: #fff; height: fit-content; // 关键!让容器高度随内容增长 } }
  • 使用flex: 1让左右列等宽;
  • height: fit-content确保容器高度能被 JS 正确读取(offsetHeight依赖于此)。

五、使用示例

父组件中这样使用:

<waterfull :list="videoList"> <template slot="left" slot-scope="{ leftList }"> <div v-for="(item, index) in leftList" :key="index"> <itemVideo :item="item"></itemVideo> </div> </template> <template slot="right" slot-scope="{ rightList }"> <div v-for="(item, index) in rightList" :key="index"> <itemVideo :item="item"></itemVideo> </div> </template> </waterfull>
  • 完全解耦渲染逻辑,父组件决定如何展示每一项;
  • 支持任意内容(图片、卡片、文字等)。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/4 23:56:22

揭秘干法刻蚀机的内核奥秘:通过3D动画解析微观工艺的宏观呈现

在半导体制造过程中&#xff0c;干法刻蚀机是一个至关重要的设备&#xff0c;它负责将复杂的电路图样细致地刻蚀到晶圆上。但干法刻蚀机的内部构造和工作原理通常对非专业人士而言显得神秘而复杂。借助3D动画技术&#xff0c;我们可以将这些复杂的技术细节直观地呈现出来&#…

作者头像 李华
网站建设 2026/3/5 17:59:50

计算机毕设java高校评优管理系统 基于Java的高校优秀评选管理平台设计与实现 Java技术驱动的高校评优信息化管理系统

计算机毕设java高校评优管理系统0u15n9&#xff08;配套有源码 程序 mysql数据库 论文&#xff09; 本套源码可以在文本联xi,先看具体系统功能演示视频领取&#xff0c;可分享源码参考。 随着高校教育信息化的不断推进&#xff0c;传统的评优管理方式已经难以满足现代高校高效、…

作者头像 李华
网站建设 2026/3/7 0:37:51

计算机毕设java高校疫情防控系统的设计与实现 基于Java的高校疫情防控管理平台开发与应用 高校疫情防控信息化系统的设计与实现

计算机毕设java高校疫情防控系统的设计与实现mkf4m9 &#xff08;配套有源码 程序 mysql数据库 论文&#xff09; 本套源码可以在文本联xi,先看具体系统功能演示视频领取&#xff0c;可分享源码参考。 随着互联网技术的飞速发展&#xff0c;高校的管理方式也在不断革新。传统的…

作者头像 李华
网站建设 2026/3/7 5:51:53

与学习相关的技巧(参数的更新)

参数的更新 神经网络的学习的目的是找到使损失函数的值尽可能小的参数。这是寻 找最优参数的问题&#xff0c;解决这个问题的过程称为最优化&#xff08;optimization&#xff09;。遗憾的是&#xff0c; 神经网络的最优化问题非常难。这是因为参数空间非常复杂&#xff0c;无法…

作者头像 李华