• [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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

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

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

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

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

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

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

    father: {1}
    // 开始
    {1},
    {2},
    {3}, 
    {4}, 
    {5}
    // 结束
    {1, children: [2, 3]}
    {2}, 
    {3},
    {4},
    {5}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    第二次循环:

    father: {2}
    // 开始
    {1, children: [{2}, {3}]}
    {2}, 
    {3},
    {4},
    {5}
    // 结束
    {1, children: [{2, children: [{4}], {3}]}
    {2, children: [{4}]}, 
    {3},
    {4},
    {5}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    这里由于 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}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    第四次循环:

    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}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

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

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

    // 最终的树形结构结果如下
    {
    	1, 
    	children: [
    		{
    			2, 
    				children: [
    					{
    						4, 
    						children: [
    							{5}
    						]}
    				]
    		}, 
    			{3}
    	]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
  • 相关阅读:
    keepalived实现nginx高可用
    影响服务器稳定性的因素:
    自己动手写数据库:关系代数和查询树执行效率的推导
    单个数据源与多数据源使用mybatisplus分页插件total一直为0的解决办法
    https的加密原理
    从北京“润”到芝加哥,工程师宝玉“滋润”成长的秘诀
    jupyter中配置多种虚拟环境
    基于springboot的疫情社区生活服务系统
    【21天学习挑战赛】冒泡排序
    树莓派4B(Ubuntu20.04)使用LCD1602液晶屏开机自动显示IP及其他信息
  • 原文地址:https://blog.csdn.net/aixintianshideshouhu/article/details/126491760