前端组件库造轮子——Tree组件开发教程
前端组件库造轮子——Tree组件开发教程
前言
本系列旨在记录前端组件库开发经验,我们的组件库项目目前已在Github开源,下面是项目的部分组件。文章会详细介绍一些造组件库轮子的技巧并且最后会给出完整的演示demo。

文章旨在总结经验,开源分享,有问题的话也希望路过的大佬指正。
组件开发流程
组件递归
Tree组件算是比较有难度的组件了,其核心功能其实就是实现树一样的联级结构。其实实现就是组件递归。
我们来复习一下递归代码
我们的递归代码实现,必然是由一个函数和调用函数组成的。同理,要实现组件递归也需要做类似的操作。
function dfs() {...
}function Main() {dfs()
}
在组件递归中,我们就需要类比递归函数的操作,我们需要用一个组件node来作为递归组件,这个组件起到主要渲染的作用,并且需要一个tree组件,来调用组件执行。
🆗,现在知道了大致思路,我们在补充一下如何编写组件。
对于递归函数,很重要的一点,我们如何让他不断递归同时让他停下来。
我们可以利用props把参数传进去,然后在渲染的时候去判断有没有孩子,如果没有孩子就不渲染,这个可以用v-if来完成。
// node 组件中
<div v-if="isRender" v-show="items.isOpen"><nodev-for="(child, index) in items.children":key="index":items="child":label="label":children="children"></node>
</div>// 判断是否要渲染
const isRender = computed(() => {return (props.items.children && props.items.children.length);
});
那这样我们就可以实现node组件的正确递归,所以我们只需在tree组件中在调用一次node组件就可以了。
<div class="tree"><nodev-for="(item, index) in copyData":key="index":items="item":label="label":children="children"></node></div>
深拷贝和初始化
还没完,我们需要对传进来的数据做一些深拷贝和初始化。
为什么要深拷贝应该知道吧?vue中props是单向数据流,我们是不能直接修改的,因此我们需要深拷贝一份来操作。
const deepCopy = (target: any, hash_table = new WeakMap()) => {if (typeof target === "object") {let clone = Array.isArray(target) ? [] : {};if (hash_table.get(target)) return hash_table.get(target);hash_table.set(target, clone);for (const key in target) {clone[key] = deepCopy(target[key], hash_table);}return clone;} else {return target;}
};
为什么要初始化呢?因为在开发tree还需要预设置很多数据,例如:是否展开?那需要实现展开的功能,那么每个节点必然需要一个isOpen来控制,除此之外,还有很多其它的功能,比如判断层级等。
interface dataType {label: string;children?: dataType[];isOpen: boolean;
}
const copyData = ref([]);
onMounted(() => {copyData.value = init(deepCopy(props.data));
});const init = (data: dataType[]) => {if (!data.length || !data) return [];let res = [];for (let i = 0; i < data.length; i++) {const child = data[i];const children = init(child[props.children] || []);const label = child[props.label];const isOpen = false;res.push({label,children,isOpen,});}return res;
};
展开和收缩
接下来,我们实现如何渲染节点和展开,这个其实很简单,我们只要在递归组件上面补充我们的想要插入的数据即可,同时绑定好事件,利用isOpen属性来实现展开收缩,我们只需要在渲染v-if上在添加v-show即可。
<ul class="tree-node"><div class="tree-node-content" @click.stop="handleToggle(items)"><span>{{ items.label }}</span></div><div v-if="isRender" v-show="items.isOpen"><nodev-for="(child, index) in items.children":key="index":items="child":label="label":children="children"></node></div>
</ul>const handleToggle = (item: any) => {item.isOpen = !item.isOpen;
};
参数设置
接下来我们来设置一些参数,因为我们不清楚用户传进来的树结构的属性是什么样子的,因此我们可以用参数来标识,比如用children来标识子节点,这些东西就可以自由发挥了。
const props = defineProps({data: {type: Array,default: () => [],},label: {type: String,default: "label",},children: {type: String,default: "children",},
});
🆗到此为止,我们就把核心功能实现完成,其实基础的功能并没有多困难,后续会补充源码。

懒加载优化
在这里我补充一个优化吧,一个简单的懒加载可以是这样的,只渲染第一层,深层的如果没有点击过就不去渲染。
这个实现思路也很容易,再增加一个isLazy参数,在初始化的时候给每个节点绑定上isLazy,在渲染时v-if增加判断isLazy就可以了。在点击的时候再把isLazy取消即可。
在参考了element的源码后,他们的懒加载还可以传入一个load函数,并用isLeft来标识动态加入新的数据。参考链接
其实实现起来也不难,我们只需要多传入一个load函数,在点击时调用该函数,并且new Promise来回调执行即可。
const init = (data: dataType[], level: number) => {if (!data.length || !data) return [];let res = [];for (let i = 0; i < data.length; i++) {const child = data[i];const children = init(child[props.children] || [], level + 1);const label = child[props.label];const isOpen = false;
+ const isLazy = props.isLazy;
+ const isLeft = child["isLeft"] || false;res.push({label,children,isOpen,isLazy,
+ isLeft,
+ level,});}return res;
};
点击后加载数据
const handleToggle = async (item: any) => {item.isOpen = !item.isOpen;if (item.isLazy) {if (item.isLeft && props.load) {await new Promise((resolve) => {props.load(item, resolve);}).then((res: any) => {for (let i = 0; i < res.length; i++) {res[i].isLazy = item.isLazy;res[i].level = item.level + 1;}item.children = res.slice();}).catch((err) => {console.log("[Tree Component] load Funtion Error", err);});}item.isLazy = false;}
};
演示demo
完整项目demo
结语
Tree组件的核心开发功能就是上面这些,其他更多的详细功能开发可以参考Hview-ui项目源码
如果想要了解更多的组件轮子开发,或者组件库开发流程,更多详细的组件开发过程更新在GitHub项目源码,最后觉得我们项目or文章不错可以点个star,点点小手支持一下,也欢迎各路大佬为我们的开源项目添砖加瓦。
相关文章:
前端组件库造轮子——Tree组件开发教程
前端组件库造轮子——Tree组件开发教程 前言 本系列旨在记录前端组件库开发经验,我们的组件库项目目前已在Github开源,下面是项目的部分组件。文章会详细介绍一些造组件库轮子的技巧并且最后会给出完整的演示demo。 文章旨在总结经验,开源分…...
java打war包、jar包方式,java运行war包、jar包方式
Java spring boot部署到生产环境有两种常见方式 1打jar包,使用了内置的tomcat服务器,流程简单 2打war包,可以放标准tomcat服务器中 jar包 1pom.xml新增 <build><plugins><plugin><groupId>org.springframework.b…...
“超级AI助手:全新提升!中文NLP训练框架,快速上手,海量训练数据,ChatGLM-v2、中文Bloom、Dolly_v2_3b助您实现更智能的应用!”
“超级AI助手:全新提升!中文NLP训练框架,快速上手,海量训练数据,ChatGLM-v2、中文Bloom、Dolly_v2_3b助您实现更智能的应用!” 1.简介 目标:基于pytorch、transformers做中文领域的nlp开箱即用…...
空时自适应处理用于机载雷达——机载阵列雷达信号环境(Matla代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
lib61850 学习笔记一 (概念)
IEC61850 定义60多种服务满足变电站通信需求。支持在线获取数据模型,也支持IED水平通信(GOOSE报文) 术语定义 间隔 bay: 变电站由据应公共功能紧密连接的子部分组成。 例如 介于进线或者 出线 和母线之间的断路器;二条母线之间…...
【深度学习】半监督学习 Efficient Teacher: Semi-Supervised Object Detection for YOLOv5
https://arxiv.org/abs/2302.07577 https://github.com/AlibabaResearch/efficientteacher 文章目录 AbstractIntroductionRelated WorkEfficient TeacherDense Detector Abstract 半监督目标检测(SSOD)在改善R-CNN系列和无锚点检测器的性能方面取得了成…...
vue3鼠标拖拽滑动效果
第一步 在utils下面新建一个directives.js文件,然后引入如下代码 const dragscroll (el) > {el.onmousedown ev > {const disX ev.clientX;const disY ev.clientY; // 需要上下移动可以加const originalScrollLeft el.scrollLeft;const originalScroll…...
08 通过从 库1 复制 *.ibd 到 库2 导致 mysql 启动报错
前言 呵呵 最近同事有这样的一个需求 需要将 库1 的一张表 复制到 库2 然后 我想到了 之前一直使用的通过复制这个库的 data 文件来进行数据迁移的思路, 是需要复制这个 库对应的 data 目录下的数据文件, 以及 ibdata1 文件 然后 我又在想 这里的场景能否也使用这里的额方式…...
一生一芯9——ubuntu22.04安装valgrind
这里安装的valgrind版本是3.19.0 下载安装包 在选定的目录下打开终端,输入以下指令 wget https://sourceware.org/pub/valgrind/valgrind-3.19.0.tar.bz2直至下载完成 解压安装包 输入下面指令解压安装包 tar -xvf valgrind-3.19.0.tar.bz2.tar.bz2注…...
STM32中BOOT的作用 (芯片死锁解决方法)
BOOT stm32中具有BOOT1和BOOT0 作用 BOOT是stm32单片机的启动模式, 通过不同组合模式,共有三种启动方式。 一般来说就是指我们下好程序后,重启芯片时,SYSCLK的第4个上升沿,BOOT引脚的值将被锁存。用户可以通过设置B…...
基于YOLOv8模型和DarkFace数据集的黑夜人脸检测系统(PyTorch+Pyside6+YOLOv8模型)
摘要:基于YOLOv8模型和DarkFace数据集的黑夜人脸检测系统可用于日常生活中检测与定位黑夜下的人脸,利用深度学习算法可实现图片、视频、摄像头等方式的目标检测,另外本系统还支持图片、视频等格式的结果可视化与结果导出。本系统采用YOLOv8目…...
C++中<iostream> 的cin >> str 和<string>的getline(cin, str) 用来读取用户输入的两种不同方式的不同点
C中<iostream> 的cin >> str 和<string>的getline(cin, str) 用来读取用户输入的两种不同方式的不同点 <string>的getline()函数语法如下【https://cplusplus.com/reference/string/string/getline/】: istream& getl…...
微信报修系统有什么优势?怎么提升企业维修工作效率与管理水平?
随着智能化时代的到来,企业、事业单位的现代化设备数量和种类不断增加,原本繁琐的报修、填写记录、检修管理等工作得以简化。从发起报修到维修,以及维修之后给予评价的整个过程,通过手机微信报修系统均能看到,既省时又…...
11.2.1-通货膨胀CPI
文章目录 1. 什么是CPI2. 在哪里获取CPI数据3. CPI的同比、环比到底是什么意思?4. 计算购买力侵蚀5. 复利计算 微不足道的小事也会引发惊人的结果, 每念及此, 我就认为世上无小事。——布鲁斯巴登(Bruce Barton) 核心内…...
服务器基础
0x01基础 介绍 可以理解为企业级的电脑,比个人使用的电脑具备更强的配置、性能、可靠性及稳定性。设计工艺和器件全部采用企业级设计,保障7*24小时稳定运行。 演进历史 处理性能 外观 发展方向 分类 按外形分类 按高度分类 按应用分类 按综合能力…...
mybatis中#{ }和${ }的区别
先说结论:二者肯定是有区别的 区别总结 ${ } 直接的 字符串 替换,在mybatis的动态 SQL 解析阶段将会进行变量替换。 #{ } 通过预编译,用占位符的方式?传值可以把一些特殊的字符进行转义,这样可以防止一些sql注入。 举例说明区…...
【真人语音】讯飞星火个人声音训练及导出下载工具V0.2.exe
【项目背景】 小编一直在尝试着短视频技术,在读文案的时候经常会读错;所以,只能用微软或者剪映的文本转语音软件。 很早之前在Github上也看到过真人人声训练的开源代码,尝试过一番之后,也是以失败告终;就…...
正中优配:创业板指大涨3.47%!减速器等概念板块掀涨停潮!
周二(8月29日),三大股指团体涨超1%。截至上午收盘,上证指数涨1.39%,报3141.82点;深证成指和创业板指别离涨2.41%和3.47%;沪深两市算计成交额6264.51亿元,总体来看,两市个股涨多跌少&…...
多功能租车平台微信小程序源码 汽车租赁平台源码 摩托车租车平台源码 汽车租赁小程序源码
多功能租车平台微信小程序源码是一款用于汽车租赁的平台程序源码。它提供了丰富的功能,可以用于租赁各种类型的车辆,包括汽车和摩托车。 这个小程序源码可以帮助用户方便地租赁车辆。用户可以通过小程序浏览车辆列表,查看车辆的详细信息&…...
spring事件和线程池区别
Spring事件(Spring Event)和线程池(Thread Pool)是两个不同的概念。 Spring事件是Spring框架中的一种机制,用于在应用程序中实现发布-订阅模式。通过定义事件和监听器,可以在特定事件发生时,通…...
Pixel Mind Decoder 异常情绪监测:在系统日志中定位用户不满信号
Pixel Mind Decoder 异常情绪监测:在系统日志中定位用户不满信号 1. 运维场景中的情绪危机 你有没有遇到过这种情况:系统运行一切正常,监控指标全绿,但用户满意度却在悄悄下滑?等到收到大量投诉时,问题已…...
KEPServerEX与SQLServer数据库的无缝集成指南
1. KEPServerEX与SQLServer集成的核心价值 在工业自动化和数据采集领域,KEPServerEX作为领先的通信平台,与SQLServer数据库的集成能够实现设备数据到关系型数据库的高效流转。这种组合特别适合需要长期存储设备运行数据、生成生产报表或进行数据分析的场…...
系统架构设计师常见高频考点总结之数据库
1. 局部数据库缓存1.1. 如何避免单点故障?(高可用设计)只要题目提到“避免单点故障”或“高可靠性”,标准答案只有一套组合拳:冗余(Redundancy):一台不够就两台。热备(Ho…...
程序员做量化交易详解
程序员做量化交易详解 量化交易是程序员将编程能力与金融市场相结合的典型应用场景。作为系统分析师,理解量化交易的全貌有助于在金融IT系统设计中把握关键要素。下面为你全面解析。 📌 一、什么是量化交易? 量化交易是指利用数学模型、统计方法和计算机技术,通过程序化…...
从synchronized到CompletableFuture:Java多线程完全进阶指南
在当今多核处理器普及的计算时代,充分利用硬件资源成为提升程序性能的关键。Java作为企业级应用的主流语言,其内置的多线程支持让并发编程变得触手可及。然而,多线程编程如同一把双刃剑——用得好,能成倍提升系统吞吐量࿱…...
计算机组成原理知识可视化:影墨·今颜生成硬件结构图解
计算机组成原理知识可视化:影墨今颜生成硬件结构图解 每次给学生讲计算机组成原理,最头疼的就是怎么把那些抽象的概念讲明白。什么“冯诺依曼结构”、“CPU流水线”、“Cache工作原理”,光靠文字和PPT里的方块图,学生听得云里雾里…...
vLLM-v0.17.1保姆级教程:vLLM + Weights Biases 实验跟踪实践
vLLM-v0.17.1保姆级教程:vLLM Weights & Biases 实验跟踪实践 1. vLLM框架简介 vLLM是一个专注于大语言模型推理和服务的开源库,以其出色的性能和易用性在开发者社区中广受欢迎。这个项目最初由加州大学伯克利分校的天空计算实验室发起࿰…...
别再死记硬背了!用游戏地图和社交网络,5分钟搞懂BFS和DFS(附C++代码)
游戏化学习:用社交网络和迷宫探险理解BFS与DFS 想象一下你正在玩一款开放世界游戏,地图被战争迷雾笼罩。每次只能看到周围一小块区域,如何高效探索整个地图?或者回忆微信里"朋友的朋友"推荐功能,系统如何找到…...
告别卡顿!用MobileNetv2+MPPTSNet-EC在树莓派上跑实时语义分割(附完整配置与性能测试)
树莓派实战:MobileNetv2MPPTSNet-EC实时语义分割全流程解析 当你在树莓派上第一次看到摄像头画面被实时分割成不同语义区域时,那种成就感绝对值得记录。本文将带你完整实现从模型选择到部署优化的全流程,用MobileNetv2MPPTSNet-EC这套组合拳&…...
【Linux】深入理解进程调度:从nice值到实时优先级(RT Priority)的进阶指南
1. Linux进程调度基础:从nice值说起 第一次接触Linux进程调度时,我被那个叫"nice值"的概念搞懵了。为什么用"nice"这个词?后来才明白,这个命名其实很形象——越"nice"的进程越谦让,愿意…...
