项目中使用 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);
这个算法使用了 js 里的原始值和引用值的的知识, 可以结合调试查看变量的变化可以更方便的理解代码。
个人觉得比较难理解的是, 看代码里没有递归也没有深度便利循环, 是如何实现最后的树形结构数据的构造的。
首先我通过打印 father 和 children 值, 可以看出一些东西, 但是在浏览器里对象嵌套太深, 并不好查看。
然后使用调试的方法, 查看每个变量值的变化。
接下来详细介绍下函数的执行的过程, 也就是树形成的过程。
下面用 Id 表示每个节点
第一次循环:
father: {1}
// 开始
{1},
{2},
{3},
{4},
{5}
// 结束
{1, children: [2, 3]}
{2},
{3},
{4},
{5}
第二次循环:
father: {2}
// 开始
{1, children: [{2}, {3}]}
{2},
{3},
{4},
{5}
// 结束
{1, children: [{2, children: [{4}], {3}]}
{2, children: [{4}]},
{3},
{4},
{5}
这里由于 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}
第四次循环:
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}
第五次循环, 同第四次, 无变化。
外层的 filter 最终会找, pid 和 rootId 相等的, 返回结果, 也就是 handleTree 函数最终的返回结果。
// 最终的树形结构结果如下
{
1,
children: [
{
2,
children: [
{
4,
children: [
{5}
]}
]
},
{3}
]
}