You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

递归遍历多级分类树生成HTML:方案选型与重复bug排查咨询

分类树遍历生成HTML字符串问题

需求

实现一个函数,遍历最多支持6级下拉的分类树,返回包含分类链接及子分类的HTML字符串。

问题

  1. 递归函数是否为该场景的最佳方案?若不是,请说明原因并给出替代方案;
  2. 当前代码遍历第一个子分类时可生成预期HTML结构(如下):
ul class="child first"><li class="parent"><a class="arrow-right" href="#">(parent name in the list)</a><ul class='child second'><li class="parent"><a class="arrow-right" href="#">(the name of the first sub child in parent)</a><ul class='child third'><li class="parent"><a href="#">last child of the previous sub child </a></li>

但遍历第二个子分类时会出现之前的子分类内容重复,示例如下:

ul class="child first"><li class="parent"><a class="arrow-right" href="#">(parent name in the list)</a><ul class='child second'><li class="parent"><a class="arrow-right" href="#">(the name of the first sub child in parent)</a><ul class='child third'>ul class="child first"><li class="parent"><a class="arrow-right" href="#">(parent name in the list)</a><ul class='child second'><li class="parent"><a class="arrow-right" href="#">(the name of the first sub child in parent)</a><ul class='child third'><li class="parent"><a href="#">last child of the previous sub child </a></li> <ul class='child second'><li class="parent"><a class="arrow-right" href="#">(the name of the second sub child in parent)</a><ul class='child third'>

已知是递归调用方式导致此问题,但不知如何调整。

当前代码

function loopingThroughTree($listAllEntries, $categoryTree, $i = 0): string
{

    $childLists =
        [
            0 => 'second',
            1 => 'third',
            2 => 'fourth',
            3 => 'fifth',
            4 => 'sixth'
        ];



    foreach ($categoryTree['sub'] as $categoryLevel) {

            $children [] = $categoryLevel;
            if ($categoryLevel['sub'] != null) {
                $listAllEntries .= '<li class="parent"><a class="'
                    . appendArrow($categoryLevel) . '" href="'
                    . $categoryLevel['link'] . '">' .
                    $categoryLevel['name'] .
                    "</a><ul class='child {$childLists[$i]}'>";
                $i++;
                $children[] = $listAllEntries;
                $listAllEntries .= loopingThroughTree($listAllEntries, $categoryLevel, $i);
            } else {
                $listAllEntries .= '<li class="parent"><a href="'
                    . $categoryLevel['link'] . '">' .
                    $categoryLevel['name'] .
                    '</a></li>';
            }
    }


    $listAllEntries .= "</ul>";

    return $listAllEntries;

}
$listAllEntries = '<ul class="child first">';
loopingThroughTree(listAllEntires,categoryTree)

注

我知道HTML字符串存在错误,目前仅关注逻辑实现。

分类树结构示例

parent => child[0]=>child[0]=>child[0]
                              child[1]
                              child[2]=>child[0]
                                        child[1]=>child[0]
                                                  child[1]
                                                  child[2]=>child[0]
                                                            child[1]
                                                            child[2]
                                        child[2]
                    child[1]
                    child[2]


          child[1]=>child[0]
                    child[1]
                    child[2]=>child[0]
                              child[1]
                              child[2]=>child[0]
                                        child[1]=>child[0]
                                                  child[1]
                                                  child[2]=>child[0]
                                                            child[1]
                                                            child[2]
                                        child[2]

问题解答

1. 递归是否为最佳方案?

递归是这类树形结构遍历的最优方案之一,因为分类树本身就是递归结构(每个节点的子节点也是同结构的树),递归写法逻辑直观、代码简洁,天然匹配树形结构的遍历需求。

如果不使用递归,替代方案是迭代法(栈/队列实现深度优先/广度优先遍历):

  • 深度优先遍历:用栈存储待处理的节点及当前层级,每次弹出栈顶节点处理,再将子节点逆序压入栈(保证顺序正确);
  • 广度优先遍历:用队列存储节点及层级,按层级顺序处理。
    但迭代法的代码复杂度比递归高,可读性差,除非遇到递归深度超过PHP默认限制(默认100层,而这里最多6层,完全没问题),否则优先用递归。

2. 递归导致内容重复的问题修复

当前代码的核心问题是把全局的$listAllEntries作为参数传入递归函数,每次递归都在原有字符串上追加,导致子递归会重复带上父级已经生成的内容。正确的做法是让每个递归函数只负责生成当前节点及其子节点的HTML片段,而不是修改全局字符串。

修复后的代码:

function buildCategoryTreeHtml($categoryTree, $level = 1): string
{
    // 层级对应的类名,level从1开始(对应根ul之后的子ul层级)
    $levelClasses = [
        1 => 'second',
        2 => 'third',
        3 => 'fourth',
        4 => 'fifth',
        5 => 'sixth'
    ];
    
    $html = '';
    
    foreach ($categoryTree['sub'] as $category) {
        // 生成当前li的开头
        $arrowClass = !empty($category['sub']) ? 'arrow-right' : '';
        $html .= '<li class="parent"><a';
        if ($arrowClass) {
            $html .= " class=\"{$arrowClass}\"";
        }
        $html .= " href=\"{$category['link']}\">{$category['name']}</a>";
        
        // 如果有子节点且未超过6级,递归生成子ul
        if (!empty($category['sub']) && $level <= 5) {
            $currentLevelClass = $levelClasses[$level] ?? '';
            $html .= "<ul class='child {$currentLevelClass}'>";
            $html .= buildCategoryTreeHtml($category, $level + 1);
            $html .= '</ul>';
        }
        
        $html .= '</li>';
    }
    
    return $html;
}

// 调用方式
$rootHtml = '<ul class="child first">' . buildCategoryTreeHtml($categoryTree) . '</ul>';

修复要点:

  • 去掉全局的$listAllEntries参数,每个递归调用只返回当前节点及子节点的HTML片段,父级负责拼接;
  • 层级参数$level从1开始,对应子ul的类名(根ul是first,第一个子ul是second,以此类推,最多到sixth对应level5);
  • 移除无用的$children数组,简化逻辑;
  • 增加层级判断$level <=5,确保最多生成6级分类;
  • 每个递归函数只处理当前节点的子节点,不会携带父级已生成内容,彻底解决重复问题。

内容的提问来源于stack exchange,提问作者Shad0w

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 22:17:55