前端组件库造轮子——Tree组件开发教程

news2025/1/21 1:04:36

前端组件库造轮子——Tree组件开发教程

前言

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

image.png

文章旨在总结经验,开源分享,有问题的话也希望路过的大佬指正。

组件开发流程

组件递归

Tree组件算是比较有难度的组件了,其核心功能其实就是实现树一样的联级结构。其实实现就是组件递归。

我们来复习一下递归代码

我们的递归代码实现,必然是由一个函数和调用函数组成的。同理,要实现组件递归也需要做类似的操作。

function dfs() {
    ...
}

function Main() {
    dfs()
}

在组件递归中,我们就需要类比递归函数的操作,我们需要用一个组件node来作为递归组件,这个组件起到主要渲染的作用,并且需要一个tree组件,来调用组件执行。

🆗,现在知道了大致思路,我们在补充一下如何编写组件。

对于递归函数,很重要的一点,我们如何让他不断递归同时让他停下来。

我们可以利用props把参数传进去,然后在渲染的时候去判断有没有孩子,如果没有孩子就不渲染,这个可以用v-if来完成。

// node 组件中
<div v-if="isRender" v-show="items.isOpen">
    <node
      v-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">
    <node
      v-for="(item, index) in copyData"
      :key="index"
      :items="item"
      :label="label"
      :children="children"></node>
  </div>

深拷贝和初始化

还没完,我们需要对传进来的数据做一些深拷贝和初始化。

为什么要深拷贝应该知道吧?vueprops是单向数据流,我们是不能直接修改的,因此我们需要深拷贝一份来操作。

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">
      <node
        v-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",
  },
});

🆗到此为止,我们就把核心功能实现完成,其实基础的功能并没有多困难,后续会补充源码。

image.png

懒加载优化

在这里我补充一个优化吧,一个简单的懒加载可以是这样的,只渲染第一层,深层的如果没有点击过就不去渲染。

这个实现思路也很容易,再增加一个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,点点小手支持一下,也欢迎各路大佬为我们的开源项目添砖加瓦。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/943291.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

三雄极光家居秋季新品发布,争滔滔不绝!

​8月28日&#xff0c;三雄极光2023家居秋季新品发布暨订货会于中山古镇盛大启幕&#xff0c;会议以“聚力革新 影势领行”为主题&#xff0c;采用线上、线下相结合的方式举行。三雄极光总裁张宇涛、副总裁林岩、营销总经理陈勤显、家居事业部副总经理赵峰等领导出席了本次会议…

hdfs操作

hadoop fs [generic options] [-appendToFile … ] [-cat [-ignoreCrc] …] [-checksum …] [-chgrp [-R] GROUP PATH…] [-chmod [-R] <MODE[,MODE]… | OCTALMODE> PATH…] [-chown [-R] [OWNER][:[GROUP]] PATH…] [-copyFromLocal [-f] [-p] [-l] [-d] … ] [-copyTo…

大学生开学必备清单|你有一份开学必备物品清单,请查收!

​又到了要开学的时候&#xff0c;面对开学季&#xff0c;很多同学还不知道需要准备哪些东西&#xff0c;为了让同学们能够准备充分大学生活&#xff0c;作为一个过来人&#xff0c;下面就来和你们唠一唠进入大学的时候&#xff0c;需要用到什么东西&#xff0c;以及有什么是开…

jumpserver堡垒机添加资产配置

目录 jumpserver堡垒机添加资产配置 1、创建jumpserver管理用户&#xff0c;登录jumpserver堡垒机 2、创建普通用户&#xff0c;管理资源服务器 3、创建特权用户&#xff0c;登录资源服务器 4、添加资源 5、资产授权 6、登录jumpserver&#xff0c;创建的jumpserver用户 7、…

【LeetCode】28 . 找出字符串中第一个匹配项的下标

28 . 找出字符串中第一个匹配项的下标&#xff08;简单&#xff09; 方法&#xff1a;双指针法 思路 使用 find 函数枚举原串 ss 中的每个字符作为「发起点」&#xff0c;每次从原串的「发起点」和匹配串的「首位」开始尝试匹配&#xff1a; 匹配成功&#xff1a;返回本次匹配…

撮合前端平台在低代码平台的落地实践 | 京东云技术团队

在京东技术的发展当下&#xff0c;不同的业务线&#xff0c;不同的区域&#xff0c;甚至于很多触达消费者的端&#xff0c;正在被中台架构能力所支撑。大家都很清楚&#xff0c;中台建设能够带来技术的规模化效应&#xff0c;具有提高业务协同、加速创新和交付速度、提高系统稳…

el-table动态生成多级表头的表格(js + ts)

展示形式&#xff1a; 详细代码&#xff1a; &#xff08;js&#xff09; <template><div><el-table :data"tableData" style"width: 100%"><el-table-column label"题目信息" align"center"><el-table-…

气传导耳机什么牌子好?盘点四款实用性不错的气传导耳机推荐

​对于追求健康和舒适的人来说&#xff0c;不入耳的气传导耳机更是一个理想的选择。气传导耳机采用不入耳设计&#xff0c;上耳效果更舒适&#xff0c;避免了对耳朵的压迫&#xff0c;还提供清晰、自然的音质。那么&#xff0c;面多市面上这么多气传导耳机&#xff0c;哪款比较…

2023年DAMA-CDGA/CDGP数据治理认证线上到这里学习

DAMA认证为数据管理专业人士提供职业目标晋升规划&#xff0c;彰显了职业发展里程碑及发展阶梯定义&#xff0c;帮助数据管理从业人士获得企业数字化转型战略下的必备职业能力&#xff0c;促进开展工作实践应用及实际问题解决&#xff0c;形成企业所需的新数字经济下的核心职业…

Docker 安装 portainer 管理神器

目录 1. 安装 Docker2. 安装 Portainer3. 创建容器4. 设置 Portainer 官网&#xff1a;https://www.portainer.io/ Portainer是一个开源的容器管理工具&#xff0c;它提供了一个直观的Web界面&#xff0c;用于管理和监控Docker容器。通过Portainer&#xff0c;您可以轻松地创建…

提前还贷计算器,可计算多次提前还款,何时还清贷款和一共要还多少利息,以及每月还款明细

这是一个提前还贷的计算器&#xff0c;特点是可以计算多次提前还款。 比如房贷120万&#xff0c;由于利息太高&#xff0c;想知道提前还款总共多久可以还清&#xff0c;一共要还多少利息。可以用这个计算器来计算。比如每年还10万&#xff0c;那么在第七次还款&#xff0c;202…

光接口和电模块

电接口 传输信号为&#xff1a;电信号&#xff0c;高电平代表1&#xff0c;低电平代表0 电口是服务器和网络中对RJ45等各种双绞线接口的统称&#xff0c;主要指铜缆&#xff0c;包括普通的网线和射频同轴电缆&#xff0c;是处理电信号的。由于这些端口都使用电作为信息的承载介…

找到字符串中所有字母异位词

力扣链接 官方题解 class Solution { public:vector<int> findAnagrams(string s, string p) {int sLen s.size(), pLen p.size();if (sLen < pLen) {return vector<int>();}vector<int> ans;vector<int> count(26);for (int i 0; i < pLen…

崭新商业理念:循环购模式的价值引领-微三云门门

尊敬的创业者们&#xff0c;我是微三云门门&#xff0c;今天我将为您详细探讨一种具有颠覆性的商业模式——循环购模式。这套私域流量裂变策略在实际应用中取得了巨大的成功&#xff0c;某些企业在短短6个月内迅速积累了400万用户&#xff01; 循环购商业模式的核心聚焦于三个…

【Python数据分析】Matplotlib小技巧!

1. 添加标题-title matplotlib.pyplot 对象中有个 title() 可以设置表格的标题。 **import** numpy **as** np **import** matplotlib.pyplot **as** plt \# 显示中文 plt.rcParams\[font.sans-serif\] \[uSimHei\] plt.rcParams\[axes.unicode\_minus\] **False** …

书单视频背景怎么编辑?分享一个简单的制作方法

在当今的数字化时代&#xff0c;视频内容已经成为了人们获取信息和娱乐的主要方式之一。书单视频也成为了越来越多人分享自己阅读体验的一种方式。为了让书单视频更加有吸引力&#xff0c;背景的设计和编辑就显得尤为重要。下面我将分享一个简单的制作方法。 使用书单视频王制作…

【Centos8配置节点免密登陆】

登录Centos8 配置免密登录 为什么需要配置免密登录&#xff0c;玩大数据&#xff0c;玩集群的朋友们&#xff0c;都需要使用RPC通讯&#xff0c;完成集群命令同步&#xff0c;数据操作通讯。要实现RPC通讯&#xff0c;就需要配置节点之间的免密登录。 # 配置登录秘钥 ssh-key…

抖店商品怎么让达人带货?说下找达人技巧和寄样后的操作,可收藏

我是王路飞。 找达人带货的玩法是公认出单快、易爆单、长久稳定的出单方式。 虽然新手可能感觉要给达人佣金&#xff0c;自己利润会降低&#xff0c;但是这种玩法可以让你快速入门&#xff0c;且能长久玩下去。 尤其是现在抖音直播间的产品全都是来自抖音小店的&#xff0c;…

wav2clip 阅读

最近看wav2clip代码和论文, 发现没人写过相关博客. 我就补上. 直接从第二章开始看: 第二章: 参考上面图1: 首先回顾了一下经典的clip模型. 然后图片中间distiling from clip采取类似结构. 但是往里面加入了MLP网络,也就是dnn, 对应图片中的Contrstive Loss Projection Layers. …

初识Java 2-1 操作符

目录 优先级 赋值 递减和递增操作符 关系操作符 逻辑操作符 字面量 字面量中的下划线 科学记数法 按位操作符 移位操作符 三元操作符 字符串操作符和 类型转换操作符 截尾和舍入 本笔记参考自&#xff1a; 《On Java 中文版》 Java的操作符大多继承自C&#xff0…