树形多级菜单数据源嵌套结构与扁平结构互转

1.前言

在日常开发中,往往会有这样的需求:根据后端返回的数据,动态渲染出一颗多级导航菜单树,类似于计算机中资源管理器的样子。如下图所示:

要实现这样的需求,其实不难,只是对后端返回的数据源有要求,如果后端返回的数据能够很清楚的表现出节点与节点之间的层级关系,那么前端实现起来就易如反掌。

2.数据源格式

一般来说,要想动态的渲染出一个树形菜单,如下所示的数据源格式对前端开发人员来说是十分友好的。

var nodes = [
    {
    name: "父节点1",
    children: [
      {
        name: "子节点11",
        children:[
          {
            name: "叶子节点111",
            children:[]
          },
          {
            name: "叶子节点112",
            children:[]
          },
          {
            name: "叶子节点113",
            children:[]
          },
          {
            name: "叶子节点114",
            children:[]
          }
        ]
      },
      {
        name: "子节点12",
        children:[
          {
            name: "叶子节点121",
            children:[]
          },
          {
            name: "叶子节点122",
            children:[]
          },
          {
            name: "叶子节点123",
            children:[]
          },
          {
            name: "叶子节点124",
            children:[]
          }
        ]
      },
      {
        name: "子节点13",
        children:[]
      }
    ]
  },
  {
    name: "父节点2",
    children: [
      {
        name: "子节点21",
        children:[
          {
            name: "叶子节点211",
            children:[]
          },
          {
            name: "叶子节点212",
            children:[]
          },
          {
            name: "叶子节点213",
            children:[]
          },
          {
            name: "叶子节点214",
            children:[]
          }
        ]
      },
      {
        name: "子节点22",
        children:[
          {
            name: "叶子节点221",
            children:[]
          },
          {
            name: "叶子节点222",
            children:[]
          },
          {
            name: "叶子节点223",
            children:[]
          },
          {
            name: "叶子节点224",
            children:[]
          }
        ]
      },
      {
        name: "子节点23",
        children:[
          {
            name: "叶子节点231",
            children:[]
          },
          {
            name: "叶子节点232",
            children:[]
          },
          {
            name: "叶子节点233",
            children:[]
          },
          {
            name: "叶子节点234",
            children:[]
          }
        ]
      }
    ]
  },
  {
    name: "父节点3",
    children:[]
  },
];

后端返回这样的数据源格式,节点之间的层级关系一目了然,前端人员拿到数据,只需进行递归遍历,并判断children.length是否等于0,等于0表明当前节点已为叶子节点,停止遍历即可。在上一篇博文vue+element UI以组件递归方式实现多级导航菜单中,动态渲染多级导航菜单,也是推荐使用这种数据源格式的。

3.问题痛点

虽然前端人员想法是好的,但是在后端,这些数据通常是存储在关系型数据库中,后端开发将数据从数据库中取出来返回给前端的数据往往这样子的:

const nodes =[
  { id:1,   pid:0,  name:"父节点1"     },           
  { id:11,  pid:1,  name:"父节点11"    },
  { id:111, pid:11, name:"叶子节点111" },
  { id:112, pid:11, name:"叶子节点112" },
  { id:113, pid:11, name:"叶子节点113" },
  { id:114, pid:11, name:"叶子节点114" },
  { id:12,  pid:1,  name:"父节点12"    },
  { id:121, pid:12, name:"叶子节点121" },
  { id:122, pid:12, name:"叶子节点122" },
  { id:123, pid:12, name:"叶子节点123" },
  { id:124, pid:12, name:"叶子节点124" },
  { id:13,  pid:1,  name:"父节点13"    },
  { id:2,   pid:0,  name:"父节点2"     },
  { id:21,  pid:2,  name:"父节点21"    },
  { id:211, pid:21, name:"叶子节点211" },
  { id:212, pid:21, name:"叶子节点212" },
  { id:213, pid:21, name:"叶子节点213" },
  { id:214, pid:21, name:"叶子节点214" },
  { id:22,  pid:2,  name:"父节点22"    },
  { id:221, pid:22, name:"叶子节点221" },
  { id:222, pid:22, name:"叶子节点222" },
  { id:223, pid:22, name:"叶子节点223" },
  { id:224, pid:22, name:"叶子节点224" },
  { id:23,  pid:2,  name:"父节点23"    },
  { id:231, pid:23, name:"叶子节点231" },
  { id:232, pid:23, name:"叶子节点232" },
  { id:233, pid:23, name:"叶子节点233" },
  { id:234, pid:23, name:"叶子节点234" },
  { id:3,   pid:0,  name:"父节点3"     }
];

其中,层级关系是通过idpid提现的,id为节点的序号,pid为该节点的父节点序号,如果为顶级节点,则其pid为0。

其实,这样的数据格式对前端来说,也不是不能用,就是没有上面那种格式用起来方便,所以,有时候前端同学就得去跪舔后端人员:

“后端大哥,能不能给我返回像这样子的数据呀?”

如果前端同学是个妹子还好,撒个娇就完事了,可如果是个汉子,后端大哥往往会回应你:

“滚,给你返回数据就不错了,还挑三拣四,想要啥样子的自己造去。”

4.解决方案

为了防止被后端同学怼(其实以上对话是博主亲身经历,摔~~~),我们前端人员果断自己动手,丰衣足食。

为了解决上述问题,博主自己写了两个方法,来实现两种数据源格式互相转化。我们姑且称理想数据格式为“嵌套型格式”,后端返回的格式为“扁平型格式”,那么两个互转方法代码如下:

/**
 * 扁平型格式转嵌套型格式
 * @param {Array} data 
 */
function FlatToNested(data) { 
  var res = [],
    tmpMap = [];
  for (let i = 0; i < data.length; i++) {
    tmpMap[data[i]['id']] = data[i];
    if (tmpMap[data[i]['pid']] && data[i]['id'] != data[i]['pid']) {
      if (!tmpMap[data[i]['pid']]['children'])
        tmpMap[data[i]['pid']]['children'] = [];
      data[i]['name'] = data[i]['name'];
      tmpMap[data[i]['pid']]['children'].push(data[i]);
    } else {
      data[i]['name'] = data[i]['name'];
      res.push(data[i]);
    }
  }
  return res;
}
/**
 * 嵌套型格式转扁平型格式
 * @param {Array} data 
 */
function NestedToFlat(data) { 
  var res = []
  for (var i = 0; i < data.length; i++) {
    res.push({
      id: data[i].id,
      name: data[i].name,
      pid: 0
    })
    if (data[i].children) {
      res = res.concat(NestedToFlat(data[i].children, data[i].id));
    }
  }
  return res;
}

5.小结

有了这两个方法,我们前端人员再也不用去跪舔后端,要啥有啥,美滋滋!

(完)

猜你喜欢

转载自www.cnblogs.com/wangjiachen666/p/10241493.html
今日推荐