前端组件库造轮子——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框架中的一种机制,用于在应用程序中实现发布-订阅模式。通过定义事件和监听器,可以在特定事件发生时,通…...
多模态2025:技术路线“神仙打架”,视频生成冲上云霄
文|魏琳华 编|王一粟 一场大会,聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中,汇集了学界、创业公司和大厂等三方的热门选手,关于多模态的集中讨论达到了前所未有的热度。其中,…...
基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...
(十)学生端搭建
本次旨在将之前的已完成的部分功能进行拼装到学生端,同时完善学生端的构建。本次工作主要包括: 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...
PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建
制造业采购供应链管理是企业运营的核心环节,供应链协同管理在供应链上下游企业之间建立紧密的合作关系,通过信息共享、资源整合、业务协同等方式,实现供应链的全面管理和优化,提高供应链的效率和透明度,降低供应链的成…...
Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务
通过akshare库,获取股票数据,并生成TabPFN这个模型 可以识别、处理的格式,写一个完整的预处理示例,并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务,进行预测并输…...
ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...
相机从app启动流程
一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...
ServerTrust 并非唯一
NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...
C# 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
