前置知识

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>