news 2026/9/24 22:12:12

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/9/23 22:29:49

误差反向传播法的实现:像搭乐高一样构建神经网络

误差反向传播法的实现&#xff1a;像搭乐高一样构建神经网络 在深度学习中&#xff0c;实现一个神经网络可以像组装乐高积木一样简单——通过组合已经实现好的各种层&#xff0c;我们可以构建出功能强大的模型。今天&#xff0c;我们将深入探讨如何通过这种方法实现神经网络&a…

作者头像 李华
网站建设 2026/9/24 19:06:54

企业OA系统安全防护全攻略:从基础加固到长效防御,十分钟构建坚不可摧的办公安全屏障

OA系统作为企业数字化办公的核心中枢&#xff0c;承载着员工信息、财务数据、业务流程、商业机密等关键资产&#xff0c;是企业生产经营的“神经中枢”。随着数字化办公的深度普及&#xff0c;OA系统已成为网络攻击的重点目标——从弱密码爆破、漏洞利用到钓鱼攻击、内部泄密&a…

作者头像 李华
网站建设 2026/9/24 18:58:00

数字取证之浏览器取证:技术体系、实操方法与实战要点

浏览器作为用户网络行为的核心载体&#xff0c;记录了访问轨迹、数据交互、账户信息、文件操作等全维度行为痕迹&#xff0c;是数字取证中电子数据固定与分析的核心环节&#xff0c;广泛应用于网络犯罪调查、民事纠纷取证、企业合规审计等场景。浏览器取证的核心目标是无篡改提…

作者头像 李华
网站建设 2026/9/24 18:57:35

MaaP:5G短消息的“心脏引擎”,政企招标暗藏的决胜密码!

作者&#xff1a;尘客首发&#xff1a;5G全域通每一条消息都是奇迹的发生。3.4亿&#xff01;中国移动三期工程、3.27亿&#xff01;中国电信全国升级——所有5G消息招标文件里&#xff0c;藏着同一个关键词&#xff1a; MaaP读懂它&#xff0c;中标率提升50%&#xff1b;忽略它…

作者头像 李华
网站建设 2026/9/24 18:54:59

专科生必备8个降AI率工具,高效避坑指南!

专科生必备8个降AI率工具&#xff0c;高效避坑指南&#xff01; AI降重工具&#xff1a;专科生论文的“隐形助手” 在当前高校教育日益重视学术规范的大背景下&#xff0c;越来越多的专科生开始关注论文的AIGC率和查重率问题。尤其是随着AI写作工具的普及&#xff0c;许多学生…

作者头像 李华
网站建设 2026/9/24 19:23:10

【读书笔记】《光环效应》

《光环效应》读书笔记 核心观点 《光环效应》揭示了一个残酷的事实&#xff1a;过去许多奉为经典的商业书籍&#xff0c;其实都只是人们的主观总结和猜测&#xff0c;因为它们全部被"光环效应"所笼罩。 什么是光环效应? 经典案例&#xff1a;乐高 vs 711 乐高的…

作者头像 李华