news 2026/7/19 7:17:54

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/7/12 10:35:48

计算机毕设java飞机票管理系统 基于Java的航空票务管理平台设计与实现 Java技术驱动的航班票务管理系统开发

计算机毕设java飞机票管理系统7h5v09 &#xff08;配套有源码 程序 mysql数据库 论文&#xff09; 本套源码可以在文本联xi,先看具体系统功能演示视频领取&#xff0c;可分享源码参考。随着互联网的普及和航空出行的日益频繁&#xff0c;传统的飞机票管理模式已难以满足现代用户…

作者头像 李华
网站建设 2026/7/17 22:35:08

HY-MT1.5-1.8B自动化部署:CI/CD流水线集成实战

HY-MT1.5-1.8B自动化部署&#xff1a;CI/CD流水线集成实战 1. 为什么需要为HY-MT1.8B构建CI/CD流水线&#xff1f; 你有没有遇到过这样的情况&#xff1a;模型在本地跑得好好的&#xff0c;一上测试环境就报CUDA内存不足&#xff1b;开发同学改了提示词模板&#xff0c;测试同…

作者头像 李华
网站建设 2026/7/18 11:26:41

2026必备!自考论文神器TOP10:AI论文工具深度测评与推荐

2026必备&#xff01;自考论文神器TOP10&#xff1a;AI论文工具深度测评与推荐 2026年自考论文写作新趋势与工具测评方向 随着人工智能技术的不断进步&#xff0c;越来越多的自考学生开始依赖AI论文工具来提升写作效率与质量。然而&#xff0c;面对市场上琳琅满目的选择&…

作者头像 李华
网站建设 2026/7/1 21:38:56

吐血推荐研究生必用TOP9 AI论文网站

吐血推荐研究生必用TOP9 AI论文网站 2026年研究生必备AI论文工具深度测评 随着人工智能技术在学术领域的广泛应用&#xff0c;越来越多的研究生开始依赖AI工具提升论文写作效率与质量。然而&#xff0c;面对市场上五花八门的AI论文网站&#xff0c;如何选择真正适合自己研究方向…

作者头像 李华
网站建设 2026/7/19 4:46:30

2.14 Docker镜像仓库管理:Harbor私有仓库搭建与镜像推送实战

2.14 Docker镜像仓库管理:Harbor私有仓库搭建与镜像推送实战 引言 镜像仓库是容器化应用的重要组成部分。Harbor是VMware开源的企业级Docker镜像仓库,提供安全、高可用的镜像管理能力。本文将详细介绍Harbor的安装、配置和使用方法。 一、镜像仓库概述 1.1 仓库类型 公共…

作者头像 李华
网站建设 2026/7/1 13:42:33

3.1 Kubernetes架构深度解析:Master和Node节点核心组件详解 (2)

3.1 Kubernetes架构深度解析:Master和Node节点核心组件详解 引言 Kubernetes是容器编排的事实标准,理解其架构是掌握Kubernetes的关键。本文将深入解析Kubernetes的Master和Node节点架构,以及各个核心组件的功能和工作原理。 一、Kubernetes架构概述 1.1 整体架构 ┌─…

作者头像 李华