JavaScript中深度排序菜单路由数据需先按parentId和id构建树形结构,再递归对每层子节点按sort升序(sort相同时回退name字典序)稳定排序。

JavaScript 中对接口返回的菜单路由数据进行深度排序,核心在于按层级关系(如 parentId 和 id)递归构建树形结构,并在每层内按指定字段(如 sort、order 或 name)稳定排序。
明确数据结构和排序目标
典型菜单数据是扁平数组,含 id、parentId、name、sort(或 order)等字段。排序需满足两个层次:
-
层级顺序:根节点(
parentId为空或为特定值如null/0)排最前,子节点严格挂在其父节点下; -
同级顺序:同一父节点下的子项,按
sort升序(或自定义字段/规则)排列,sort相同时可回退到name字典序。
构建树形结构 + 递归排序
推荐一步到位:先建立父子映射,再从根节点出发,递归生成已排序的树。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
function buildSortedMenuTree(list, options = {}) {
const {
idKey = 'id',
parentIdKey = 'parentId',
sortKey = 'sort',
fallbackSortKey = 'name',
rootParentId = null // 根节点的 parentId 值
} = options;
// 1. 建立 id → item 映射,便于快速查找
const map = new Map();
list.forEach(item => map.set(item[idKey], { ...item }));
// 2. 按 parentId 分组子节点
const childrenMap = new Map();
list.forEach(item => {
const pid = item[parentIdKey];
if (!childrenMap.has(pid)) childrenMap.set(pid, []);
childrenMap.get(pid).push(item);
});
// 3. 定义比较函数(用于同级排序)
const compare = (a, b) => {
const aVal = a[sortKey] ?? a[fallbackSortKey];
const bVal = b[sortKey] ?? b[fallbackSortKey];
if (aVal < bVal) return -1;
if (aVal > bVal) return 1;
return 0;
};
// 4. 递归构建并排序
function build(nodeId) {
const children = childrenMap.get(nodeId) || [];
// 同级排序
children.sort(compare);
// 递归处理每个子节点的后代
return children.map(child => ({
...child,
children: build(child[idKey])
}));
}
return build(rootParentId);
}
// 使用示例
const menuData = [
{ id: 1, parentId: null, name: '系统管理', sort: 3 },
{ id: 2, parentId: 1, name: '用户管理', sort: 2 },
{ id: 3, parentId: 1, name: '角色管理', sort: 1 },
{ id: 4, parentId: null, name: '内容管理', sort: 1 },
{ id: 5, parentId: 4, name: '文章列表', sort: 1 },
{ id: 6, parentId: 4, name: '分类管理', sort: 2 }
];
const sortedTree = buildSortedMenuTree(menuData);
console.log(sortedTree);
// 输出:根为「内容管理」→「系统管理」;每组内部按 sort 排序
若需扁平化结果(如用于 router.addRoute)
有些场景需要保持扁平结构但保证深度优先遍历顺序(即先根、再子、子按序、子的子也按序),可用 DFS 遍历已排序的树:
立即学习“Java免费学习笔记(深入)”;
function flattenMenuTree(tree) {
const result = [];
function dfs(nodes) {
for (const node of nodes) {
result.push(node);
if (Array.isArray(node.children) && node.children.length) {
dfs(node.children);
}
}
}
dfs(tree);
return result;
}
// 得到:[{内容管理}, {文章列表}, {分类管理}, {系统管理}, {角色管理}, {用户管理}]
const flatSorted = flattenMenuTree(sortedTree);
注意事项与优化点
⚠️ 注意:真实项目中需考虑边界情况:
- 循环引用(如 A 的 parentId 是 B,B 的 parentId 是 A)——建议加访问标记或层级限制防栈溢出;
- 缺失父节点(子项 parentId 指向不存在的 id)——可过滤或归入根节点;
- 后端未传
sort字段时,用name或默认序号兜底; - 对大型菜单(>500 条),避免重复创建对象,可原地排序 + 引用复用。


















