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

这篇具有很好参考价值的文章主要介绍了前端组件库造轮子——Tree组件开发教程。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前端组件库造轮子——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,点点小手支持一下,也欢迎各路大佬为我们的开源项目添砖加瓦。文章来源地址https://www.toymoban.com/news/detail-679513.html

到了这里,关于前端组件库造轮子——Tree组件开发教程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C++ Qt开发:Tab与Tree组件实现分页菜单

    Qt 是一个跨平台C++图形界面开发库,利用Qt可以快速开发跨平台窗体应用程序,在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置,实现图形化开发极大的方便了开发效率,本章将重点介绍 tabWidget 选择夹组件与 TreeWidget 树形选择组件,的常用方法及灵活运用。 QTabW

    2024年02月04日
    浏览(32)
  • uni-app之Cover-View组件详细使用教程

    在 UniApp 中,Cover-View 组件是一种用于展示覆盖在页面上方的视图元素的组件。它可以用于创建各种遮罩、弹出层、悬浮按钮等效果,提供了更多自定义样式和交互的可能性。本教程将详细介绍 Cover-View 组件的用法和示例代码。 步骤1:创建一个 UniApp 项目 首先,确保已经安装

    2024年02月07日
    浏览(146)
  • 开发需求15-使用el-checkbox组件实现el-tree组件的父子关联关系(非全选/全不选)

    需求描述: 大家都知道el-tree可以很明显的通过选中来体现上下节点的父子选中状态,那么如果要求把后端把el-tree的数据结构,通过一个展开的list返回给你,使用el-checkbox组件渲染每一个节点,同时要求选中某一个节点,同时可以选中其父节点和子节点;取消也是一样。 思路

    2024年04月17日
    浏览(43)
  • 什么是组件,以及前端各种框架组件的使用方法

    🙂博主:小猫娃来啦 🙂文章核心: 介绍什么是组件,以及前端各种框架组件的使用方法 ⭐组件就像是搭积木一样的东西,可以用来构建软件或者系统。每个组件都有自己独立的功能和任务,就像一个小小的部件。你可以把这些小部件组合在一起,形成一个完整的应用程序

    2024年02月12日
    浏览(40)
  • 前端文件上传的几种交互造轮子

    前端文件上传本来是一个常规交互操作,没什么特殊性可言,但是最近在做文件上传,需要实现截图粘贴上传,去找了下有没有什么好用的组件,网上提供的方法有,但是没找完整的组件来支持cv上传,经过了解发现可以用剪贴板功能让自己的cv实现文件上传,于是自己就整合

    2024年02月11日
    浏览(33)
  • 造个轮子-任务调度执行小框架-IOC容器实现

    忙里偷闲,今天终于是把概率论这块骨头干下来了。所以的话,留了点时间,把整个项目的结构和基本的功能给实现以下。通过昨天的一个功能的一个设计,我想应该可以明白我想干啥吧。这里的话,重复一下,那就是俺们要搞一个任务执行框架。 这个框架到底有啥用?举个

    2024年02月13日
    浏览(30)
  • 造个轮子-任务调度执行小框架-任务清单解析实现

    okey~每日编码一坤时,昨天的话我们已经实现了这个框架的IOC容器。通过这个IOC容器,我们就可以非常轻松地进行后续的操作,于是,我们接着这个工作,去完成这个任务清单的解析。 昨天的话,阐述了一下这个框架解决了哪些问题,那么接下来,是如何使用这个家伙。以及

    2024年02月13日
    浏览(33)
  • LeetCode //C - 199. Binary Tree Right Side View

    Given the root of a binary tree, imagine yourself standing on the right side of it, return the values of the nodes you can see ordered from top to bottom.   Example 1: Input: root = [1,2,3,null,5,null,4] Output: [1,3,4] Example 2: Input: root = [1,null,3] Output: [1,3] Example 3: Input root = [] Output [] Constraints: The number of nodes in the tree is in t

    2024年01月20日
    浏览(28)
  • 05-1_Qt 5.9 C++开发指南_Model/View结构基础(基本原理;数据模型;试图组件;代理)

    Model/View(模型/视图) 结构是 Qt 中用界面组件显示与编辑数据的一种结构,视图 (View)是显示和编辑数据的界面组件,模型 (Model) 是视图与原始数据之间的接口。 Model/View 结构的典型应用是在数据库应用程序中,例如数据库中的一个数据表可以在一个 OTableView 组件中显示和编辑。

    2024年02月17日
    浏览(38)
  • 组件化、跨平台…未来前端框架将如何演进?

    前端框架在过去几年间取得了显著的进步和演进。前端框架也将继续不断地演化,以满足日益复杂的业务需求和用户体验要求。从全球web发展角度看,框架竞争已经从第一阶段的前端框架之争(比如Vue、React、Angular等),过渡到第二阶段的框架之争(比如Next、Nuxt、Remix、小程

    2024年02月14日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包