将扁平数据转换为树形结构的方法指南
在实际前端开发中,后端经常返回的是扁平化结构的列表数据,例如菜单、组织架构、文件目录等。为了在前端以树形结构进行渲染(如 Vant 的 TreeSelect、ElementUI 的 Tree),我们就需要将这类数据格式化为嵌套结构的树形数据。
本文将带你从思路剖析到代码实现,并提供兼容性方案与完整演示,帮助你掌握数据结构转化的核心技巧。
一、问题背景
后端返回的原始数据如下所示,每一项通过 parentId 字段指示其所属的父节点:
[
{ "id": 48, "parentId": 47, "workspaceName": "工艺计划书合集", "workspaceCode": "A8-B1" },
{ "id": 47, "parentId": null, "workspaceName": "工艺计划书", "workspaceCode": "A8" },
...
]
这是一种扁平数据结构(Flat List),而我们需要的,是如下这种具有嵌套结构的树形结构(Tree):
[
{
"id": 47,
"workspaceName": "工艺计划书",
"children": [
{
"id": 48,
"workspaceName": "工艺计划书合集"
}
]
},
...
]
二、思路分析:如何从列表构造出树?
数据转树的核心本质:找到每个节点的父节点,并将其挂载为子节点
步骤分为两轮遍历:
-
首先遍历整个数组,把每个节点放进一个 Map 中(通过 id 映射);
-
再次遍历数组,判断每个节点的 parentId 是否在 Map 中:
-
是:将当前节点 push 到其父节点的 children 数组中;
-
否:说明是根节点,加入结果树的根节点数组。
-
三、现代写法(ES6 Map 实现)
function listToTree(data) {
const map = new Map();
const tree = [];
data.forEach(item => {
item.children = [];
map.set(item.id, item);
});
data.forEach(item => {
if (item.parentId !== null && map.has(item.parentId)) {
map.get(item.parentId).children.push(item);
} else {
tree.push(item);
}
});
return tree;
}
✅ 优势:结构清晰,利用 Map 高效查找,适用于现代浏览器环境(IE 不支持)
四、兼容写法(若依框架风格)
如果项目需要兼容老旧浏览器(如 IE11),可以采用传统对象存储方式:
export function handleTree(data, id = 'id', parentId = 'parentId', children = 'children') {
const childrenMap = {};
const nodeMap = {};
const tree = [];
for (let item of data) {
const pid = item[parentId];
if (!childrenMap[pid]) childrenMap[pid] = [];
childrenMap[pid].push(item);
nodeMap[item[id]] = item;
}
for (let item of data) {
const pid = item[parentId];
if (!nodeMap[pid]) tree.push(item);
}
const adaptChildren = node => {
if (childrenMap[node[id]]) {
node[children] = childrenMap[node[id]];
node[children].forEach(child => adaptChildren(child));
}
};
tree.forEach(root => adaptChildren(root));
return tree;
}
✅ 优势:兼容旧浏览器、适合低版本 Vue 项目(如 Vue2.x + Element)
五、两种方案对比总结
维度 | ES6 写法(Map) | 若依写法(传统对象) |
---|---|---|
性能 | 快(O(n)查找) | 稍慢(对象查找) |
代码简洁性 | 更简洁 | 更冗长 |
浏览器兼容性 | 现代浏览器(Chrome/Edge) | 包括 IE 浏览器 |
场景适用 | Vue3/Vite | Vue2/老旧项目 |
六、完整运行示例(HTML + JS)
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<title>树形结构转换示例</title>
</head>
<body></body>
<script>
const data = [
{ id: 48, parentId: 47, workspaceName: '工艺计划书合集', workspaceCode: 'A8-B1' },
{ id: 47, parentId: null, workspaceName: '工艺计划书', workspaceCode: 'A8' },
{ id: 26, parentId: null, workspaceName: '0501DKD工作指导书', workspaceCode: 'A2' },
{ id: 29, parentId: 26, workspaceName: '月度计划工艺包装方案A', workspaceCode: 'A2-B1' },
{ id: 35, parentId: 26, workspaceName: '月度计划工艺包装方案B', workspaceCode: 'A2-B2' },
];
function listToTree(data) {
const map = new Map();
const tree = [];
data.forEach((item) => {
item.children = [];
map.set(item.id, item);
});
data.forEach((item) => {
if (item.parentId !== null && map.has(item.parentId)) {
map.get(item.parentId).children.push(item);
} else {
tree.push(item);
}
});
return tree;
}
console.log('Tree:', listToTree(data));
</script>
</html>
最后,如果您觉得本文对你有帮助,欢迎 👍 点赞 / ⭐ 收藏 / 💬 评论!