[javascrip]构造树形数据实现

背景

项目中使用 element_ui 的 tree 组件, 需要自己构造树形结构数据传入。
element-ui 树形组件文档链接
在ruoyi的项目中找到参考代码, 感觉实现的很好, 分享下自己的理解过程, 源码本身的实现有个小 bug, 顺便处理下。

分析

/**
 * 构造树型结构数据
 * @param {*} data 数据源
 * @param {*} id id字段 默认 'id'
 * @param {*} parentId 父节点字段 默认 'parentId'
 * @param {*} children 孩子节点字段 默认 'children'
 * @param {*} rootId 根Id 默认 0
 */
function handleTree(data, id, parentId, children, rootId) {
  id = id || 'id'
  parentId = parentId || 'parentId'
  children = children || 'children';
  console.log(children);
  rootId = rootId || 0
  // 对源数据深度克隆
  const cloneData = JSON.parse(JSON.stringify(data))
  // 循环所有项
  const treeData = cloneData.filter(father => {
    const branchArr = cloneData.filter(child => {
      // 返回每一项的子级数组
      return father[id] === child[parentId]
    })
    // branchArr.length > 0 ? father.children = branchArr : '';
    // 上面注释的是源代码的写法, 这里有些问题, js 里面使用 `对象.属性`访问对象的属性, 最终会转化为 `对象['属性']`的形式, 所以这里写法会导致无论 children 传入任意参数, 最终结果都是 children, 而不是传入的
    branchArr.length > 0 ? father[children] = branchArr : '';
    return father[parentId] === rootId
  });
  return treeData != '' ? treeData : data
}

const data = [{
  id: 1,
  name: '父亲',
  pid: 0,
}, {
  id: 2,
  name: '哥哥',
  pid: 1
}, {
  id: 3,
  name: '弟弟',
  pid: 1
}, {
  id: 4,
  name: '哥哥的儿子',
  pid: 2
}, {
  id: 5,
  name: 'test',
  pid: 4
}]

let res = handleTree(data, 'id', 'pid', 'childrens', 0);
console.log(res);

这个算法使用了 js 里的原始值和引用值的的知识, 可以结合调试查看变量的变化可以更方便的理解代码。

个人觉得比较难理解的是, 看代码里没有递归也没有深度便利循环, 是如何实现最后的树形结构数据的构造的。

首先我通过打印 father 和 children 值, 可以看出一些东西, 但是在浏览器里对象嵌套太深, 并不好查看。

然后使用调试的方法, 查看每个变量值的变化。

接下来详细介绍下函数的执行的过程, 也就是树形成的过程。

下面用 Id 表示每个节点
第一次循环:

father: {1}
// 开始
{1},
{2},
{3}, 
{4}, 
{5}
// 结束
{1, children: [2, 3]}
{2}, 
{3},
{4},
{5}

第二次循环:

father: {2}
// 开始
{1, children: [{2}, {3}]}
{2}, 
{3},
{4},
{5}
// 结束
{1, children: [{2, children: [{4}], {3}]}
{2, children: [{4}]}, 
{3},
{4},
{5}

这里由于 father 指向的是 cloneData 的第二个元素, 是引用值, 所以对引用值修改, 所有使用该引用值的地方都会变化, {1, children: [{2}, {3}]} 这里的 {2}{2} 指向同一块内存空间。

第三次:

father: {3}
// 开始
{1, children: [{2, children: [{4}]}, {3}]}
{2, children: [{4}]}, 
{3},
{4},
{5}
// 结束
{1, children: [{2, children: [{4}]}, {3}]}
{2, children: [{4}]}, 
{3},
{4},
{5}

第四次循环:

father: {3}
// 开始
{1, children: [{2, children: [{4}]}, {3}]}
{2, children: [{4}]}, 
{3},
{4},
{5}
// 结束
{1, children: [{2, children: [{4, children: [{5}]}]}, {3}]}
{2, children: [{4}]}, 
{3},
{4, children: [{5}]},
{5}

第五次循环, 同第四次, 无变化。

外层的 filter 最终会找, pid 和 rootId 相等的, 返回结果, 也就是 handleTree 函数最终的返回结果。

// 最终的树形结构结果如下
{
	1, 
	children: [
		{
			2, 
				children: [
					{
						4, 
						children: [
							{5}
						]}
				]
		}, 
			{3}
	]
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值