本文详解如何用递归函数正确渲染深度可达 6 层的分类树结构,解决常见递归调用导致的 html 重复嵌套、层级错乱问题,并提供健壮、可维护的 php 实现方案。
在构建电商、CMS 或知识库等系统时,常需将具有父子关系的分类数据(如「电子产品 → 手机 → 智能手机 → 国产旗舰」)渲染为带层级缩进与折叠箭头的 HTML 下拉菜单。此时,递归是天然且最优的解决方案——因为树形结构本质上是自相似的:每个节点都可能拥有结构相同的子树。相比迭代(需手动维护栈)、扁平化预处理(破坏语义、增加内存开销)或模板引擎递归标签(灵活性低、调试困难),递归函数逻辑清晰、代码简洁、易于扩展层级与样式。
但正如提问者所遇,错误的递归设计极易引发严重副作用:HTML 标签重复闭合、父级内容被多次拼接、层级类名(second/third)错位、甚至无限循环。核心症结在于:混淆了“累积字符串”与“递归返回值”的职责,且未隔离每层递归的输出作用域。
以下是一个修复后、生产可用的递归渲染函数(已优化逻辑、修正语法、增强鲁棒性):
function renderCategoryTree(array $categoryTree, int $depth = 0): string
{
// 定义各层级对应的 CSS 类名(支持最多 6 级:first → sixth)
$levelClasses = [
0 => 'first',
1 => 'second',
2 => 'third',
3 => 'fourth',
4 => 'fifth',
5 => 'sixth'
];
// 当前层级对应类名,超出则取最后一个(防越界)
$currentClass = $levelClasses[$depth] ?? $levelClasses[5];
// 初始化当前层级的 - 开始标签
$html = "
- 及其内部链接 if ($hasChildren) { // 有子级:添加箭头类,并递归渲染子树(深度 +1) $arrowClass = appendArrow($child); // 假设该函数返回 'arrow-right' 或空字符串 $html .= "
- {$name}"; $html .= renderCategoryTree($child, $depth + 1); // ✅ 关键:仅传入子节点,不传递累积字符串 $html .= " "; } else { // 无子级:普通链接 $html .= "
- {$name} "; } } // 闭合当前层级的
- ";
// 遍历当前节点的所有直接子分类
foreach ($categoryTree['sub'] as $child) {
// 确保必要字段存在,避免 Notice 错误
$name = $child['name'] ?? 'Untitled';
$link = $child['link'] ?? '#';
$hasChildren = !empty($child['sub']) && is_array($child['sub']);
// 构建
-
$html .= "
✅ 关键改进点解析: