# 树形结构数据处理算法

假如后端接口返回给我们的数据是下面这个样子的,要求我们前端页面用树形结构展示出来,你会怎么做?

let datas = [
    {
        name: '第一阶段 JS 基础入门',
        children: [
            {
                name: '第一章 走入 JavaScript 的世界',
                children: [
                    {
                        name: 'JavaScript简介'
                    },
                    {
                        name: 'JavaScript可以做什么?'
                    },
                    {
                        name: 'Web中哪些时候需要用到'
                    }
                ]
            },
            {
                name: '第二章 玩转属性操作',
                children: [
                    {
                        name: '什么是属性'
                    },
                    {
                        name: 'JS 属性操作'
                    },
                    {
                        name: 'JS 中两种操作属性的方法'
                    }
                ]
            }
        ]
    },
    {
        name: '第二阶段  JS核心功能探秘',
        children: [
            {
                name: '第一章 ECMAScript 6',
                children: [
                    {
                        name: 'let 和 const'
                    },
                    {
                        name: '解构赋值'
                    },
                    {
                        name: '展开运算符'
                    }
                ]
            },
            {
                name: '第二章 DOM',
                children: [
                    {
                        name: '什么是DOM'
                    },
                    {
                        name: 'DOM 节点类型'
                    },
                    {
                        name: 'DOM 树及DOM关系'
                    }
                ]
            }
        ]
    },
    {
        name: '第三阶段 JavaScript 高级进阶',
        children: [
            {
                name: '第一章 面向对象&继承&组件开发'
            },
            {
                name: '第二章 Node.js'
            },
            {
                name: '第三章 客户端信息存储'
            }
        ]
    }
];

先来看下我们想实现的一个最终效果

下面我们开始分析,页面我们用一个ulli的结构来展示

<ul id="ulList"></ul>

一开始,我们想到的可能是用一个for循环来遍历

let ulList = document.getElementById('ulList');

let html = '';

datas.forEach(item => {
  html += `<li>${ item.name }</li>`;
});

ulList.innerHTML = html;

这时候页面展示效果如下

我们还可以使用map方法来优化下我们的代码,让代码更简洁

let ulList = document.getElementById('ulList');

ulList.innerHTML = datas.map(item => {
  return `<li>${ item.name }</li>`;
}).join('');

到这里,我们只展示了第一层的数据,如果要把所有层级都展示,就要使用递归了,递归简单来说就是函数内部自己调用自己。

我们需要把代码改造一下

let ulList = document.getElementById('ulList');

ulList.innerHTML = createHTML(datas);

function createHTML(items) {
  return items.map(item => {
    let html = `
      <li>${item.name}</li>
    `;
    // 判断是否还有下一层级且必须是一个数组
    if (item.children && Array.isArray(item.children)) {
      // 递归调用
      html += createHTML(item.children);
    }
    return html;
  }).join('');
}

再来看下页面效果

最后我们需要解决的是层级缩进的问题,我们再来修改一下我们的代码

let ulList = document.getElementById('ulList');

ulList.innerHTML = createHTML(datas);

function createHTML(items, level = 0) { // 增加一个level参数,默认值为0
  return items.map(item => {
    // 每一个层级都比上一个层级缩进30px
    let html = `
      <li style="padding-left: ${level * 30}px;">${item.name}</li>
    `;
    if (item.children && Array.isArray(item.children)) {
      // 递归调用
      // 每次需要递归的时候,让level参数递增1
      html += createHTML(item.children, level + 1);
    }
    return html;
  }).join('');
}

最后就达到我们想要的效果了。

注意

上面例子中有使用了ES6增加的新特性:箭头函数模板字符串参数默认值,所以请在支持ES6语法的浏览器中测试。