导航


HTML

CSS

JavaScript

浏览器 & 网络

版本管理

框架

构建工具

TypeScript

性能优化

软实力

算法

UI、组件库

Node

冷门技能

针对性攻坚(TODO)


前置知识

mock 数据

[
  { id: 1, pid: -1, name: "一级菜单A", rank: 1 },
  { id: 2, pid: -1, name: "一级菜单B", rank: 1 },
  { id: 3, pid: -1, name: "一级菜单C", rank: 1 },
  { id: 4, pid: 1, name: "二级菜单A-A", rank: 2 },
  { id: 5, pid: 1, name: "二级菜单A-B", rank: 2 },
  { id: 6, pid: 2, name: "二级菜单B-A", rank: 2 },
  { id: 7, pid: 4, name: "三级菜单A-A-A", rank: 3 },
  { id: 8, pid: 7, name: "四级菜单A-A-A-A", rank: 4 },
  { id: 9, pid: 8, name: "五级菜单A-A-A-A-A", rank: 5 },
  { id: 10, pid: 9, name: "六级菜单A-A-A-A-A-A", rank: 6 },
  { id: 11, pid: 10, name: "七级菜单A-A-A-A-A-A-A", rank: 7 },
  { id: 12, pid: 11, name: "八级菜单A-A-A-A-A-A-A-A", rank: 8 },
  { id: 13, pid: 12, name: "九级菜单A-A-A-A-A-A-A-A-A", rank: 9 },
  { id: 14, pid: 13, name: "十级菜单A-A-A-A-A-A-A-A-A-A", rank: 10 },
]

方案1 - 递归

思路:找儿子

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>树形结构</title>
</head>
<body>
  <script>
    let data = [
		  { id: 1, pid: -1, name: "一级菜单A", rank: 1 },
		  { id: 2, pid: -1, name: "一级菜单B", rank: 1 },
		  { id: 3, pid: -1, name: "一级菜单C", rank: 1 },
		  { id: 4, pid: 1, name: "二级菜单A-A", rank: 2 },
		  { id: 5, pid: 1, name: "二级菜单A-B", rank: 2 },
		  { id: 6, pid: 2, name: "二级菜单B-A", rank: 2 },
		  { id: 7, pid: 4, name: "三级菜单A-A-A", rank: 3 },
		  { id: 8, pid: 7, name: "四级菜单A-A-A-A", rank: 4 },
		  { id: 9, pid: 8, name: "五级菜单A-A-A-A-A", rank: 5 },
		  { id: 10, pid: 9, name: "六级菜单A-A-A-A-A-A", rank: 6 },
		  { id: 11, pid: 10, name: "七级菜单A-A-A-A-A-A-A", rank: 7 },
		  { id: 12, pid: 11, name: "八级菜单A-A-A-A-A-A-A-A", rank: 8 },
		  { id: 13, pid: 12, name: "九级菜单A-A-A-A-A-A-A-A-A", rank: 9 },
		  { id: 14, pid: 13, name: "十级菜单A-A-A-A-A-A-A-A-A-A", rank: 10 },
		];

    function formatData(data) {
      let parents = [], children = [];

      // 先把第一层 父亲们 和 儿子们 分开来(pid = -1的都是父亲)
      data.forEach(item => {
        item.pid === -1
          ? parents.push(item)
          : children.push(item);
      });

      // 调用递归函数
      dataToTree(parents, children);

      // 最终返回 树结构
      return parents;

      // 接收两个参数,一个是 父亲们,一个是 儿子们
      function dataToTree(parents, children) {
        parents.forEach(p => {
          children.forEach((c, i) => {
            // 当儿子找到了父亲
            if (c.pid === p.id) {
              // 拷贝一下所有儿子
              const _children = JSON.parse(JSON.stringify(children));
              // 从儿子中剔除当前已经找到父亲的儿子
              _children.splice(i, 1);
              // 去找当前的 儿子,还有没有自己的儿子
              dataToTree([c], _children);

              // 把儿子放到父亲那儿去
              if (p.children) {
                p.children.push(c);
              } else {
                p.children = [c];
              }
            }
          });
        });
      }
    }
    const tree = formatData(data);
    console.log(tree);
  </script>
</body>
</html>

方案2 - 非递归

思路:找父亲

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>树形结构</title>
</head>
<body>
  <script>
    let data = [
		  { id: 1, pid: -1, name: "一级菜单A", rank: 1 },
		  { id: 2, pid: -1, name: "一级菜单B", rank: 1 },
		  { id: 3, pid: -1, name: "一级菜单C", rank: 1 },
		  { id: 4, pid: 1, name: "二级菜单A-A", rank: 2 },
		  { id: 5, pid: 1, name: "二级菜单A-B", rank: 2 },
		  { id: 6, pid: 2, name: "二级菜单B-A", rank: 2 },
		  { id: 7, pid: 4, name: "三级菜单A-A-A", rank: 3 },
		  { id: 8, pid: 7, name: "四级菜单A-A-A-A", rank: 4 },
		  { id: 9, pid: 8, name: "五级菜单A-A-A-A-A", rank: 5 },
		  { id: 10, pid: 9, name: "六级菜单A-A-A-A-A-A", rank: 6 },
		  { id: 11, pid: 10, name: "七级菜单A-A-A-A-A-A-A", rank: 7 },
		  { id: 12, pid: 11, name: "八级菜单A-A-A-A-A-A-A-A", rank: 8 },
		  { id: 13, pid: 12, name: "九级菜单A-A-A-A-A-A-A-A-A", rank: 9 },
		  { id: 14, pid: 13, name: "十级菜单A-A-A-A-A-A-A-A-A-A", rank: 10 },
		];
    function formatData2(data) {
      let _data = JSON.parse(JSON.stringify(data));

      return _data.filter(p => {
        // 遍历每个对象,找自己的儿子们
        let _arr = _data.filter(c => c.pid === p.id);
        _arr.length && (p.children = _arr);
        return p.pid === -1;
      });
    }

    const tree = formatData2(data);
    console.log(tree);
  </script>

	<script>
	// 或者找自己的父亲
	function formatData(arr) {
	  return arr.filter((v) => {
	    if (v.pid === -1) {
	      return v;
	    } else {
	      const parent = arr.find((c) => c.id === v.pid);
	      if (parent.children) {
	        parent.children.push(v);
	      } else {
	        parent.children = [v];
	      }
	    }
	  });
	}
	</script>
</body>
</html>