游乐游手机版
首页/前端开发/文章详情

高性能递归算法实现HTML嵌套列表转换为树状JSON结构

时间:2026-06-26 06:59
先说结论:处理嵌套列表解析的核心策略是:优先使用迭代+栈模拟递归,而非纯递归。原因很简单:当浏览器中DOM深度达到20层以上时,递归极易引发栈溢出——这并非理论假设,而是实际开发中频繁遇到的真实现象。优化的目标不是追求速度,而是确保不遗漏节点、不混淆层级、不发生栈溢出。 浏览器中DOM深度一旦超过5
先说结论:处理嵌套列表解析的核心策略是:优先使用迭代+栈模拟递归,而非纯递归。原因很简单:当浏览器中DOM深度达到20层以上时,递归极易引发栈溢出——这并非理论假设,而是实际开发中频繁遇到的真实现象。优化的目标不是追求速度,而是确保不遗漏节点、不混淆层级、不发生栈溢出。HTML嵌套列表转换为树状JSON结构的高性能递归算法 浏览器中DOM深度一旦超过50层,就很容易触发递归调用栈限制,而在实际页面中,嵌套列表的深度通常在8到12层之间。在这种情况下,纯递归基本无法胜任。 ### 如何安全提取 `
  • ` 的父子关系 需要注意的是,DOM树与JSON树的结构并不完全等同。`
  • ` 元素内部可能混杂文本、链接、图标等各种内容,且它的子 `
      ` 不一定紧跟在后面。这意味着不能简单依赖 `children` 遍历,必须显式定位每个 `
    • ` 下的第一个 `
        `,并将其作为子节点容器。 具体操作上: * 首先通过 `querySelectorAll('li')` 获取所有 `
      • ` 元素,并按照DOM顺序逐个处理。这样能有效避免因CSS属性 `display:none` 或JavaScript动态插入导致的节点遗漏。 * 对每个 `
      • `,使用 `nextElementSibling` 向下查找最近的 `
          `,而不是用 `li.querySelector('ul')`。后者容易误抓兄弟节点下的子菜单,造成层级混乱。 * 如果找到了对应的 `
            `,则递归解析它;如果未找到,则将 `children` 设置为空数组。 ### `parseListToTree()` 函数必须隔离作用域与状态 一个常见的失误点:将 `result` 数组或 `currentNode` 对象传入递归函数进行累加,导致所有层级共享同一个引用,子节点被重复推送到父节点两次。正确的做法是:让每一层递归都返回一个全新的数组,由上一层决定是否合并。 函数签名应设计为 `function parseListToTree(ulElement) { ... return childrenArray; }`,坚决不接受外部变量注入。 * 每个 `
          • ` 的文本内容需要清理:剔除换行、多余空格、` `,然后使用 `textContent.trim()` 而不是 `innerText`(后者受CSS影响,结果可能不准确)。 * 如果需要保留原始HTML片段(例如含有图标标签),可以使用 `innerHTML.replace(/< \/?ul[^]*?>/gi, '')` 剥离包裹标签。不过要注意XSS风险,建议添加白名单过滤。 ### 深度超过20层时,改用迭代+栈模拟递归 虽然Chrome V8的默认调用栈限制大约为10000帧,但实际测试表明,DOM深度一旦超过20层,就可能触发 `RangeError: Maximum call stack size exceeded`,尤其在旧版Safari中更加敏感。这种情况下必须切换策略。 使用数组模拟栈:`const stack = [{ ul: rootUl, depth: 0, parent: null }];` 每次弹出一个节点,处理其下的 `
          • `,然后将子 `
              ` 推入栈中。 * 每一层生成的对象都必须带有 `id` 字段(可以用 `Math.random().toString(36).substr(2, 9)` 生成轻量唯一键),否则后续无法关联父子关系。 * 迭代版本的性能会略低(多一次循环),但内存可控,没有栈溢出风险。特别适合CMS导出菜单、文档大纲等深度不可控的场景。 真正的核心难点不在于如何编写递归,而在于判断何时应该避免递归。当DOM层级不断加深时,就需要认真考虑:是否应该采用平面列表配合depth字段来替代嵌套JSON结构?许多前端树组件(如 antd Tree)在内部早已将数据扁平化处理,渲染时才按depth计算缩进。这一细节常被忽视,却对长期维护影响深远。我的结论是:与其在20层以上强行使用递归,不如从一开始就转向扁平化数据结构。
  • 来源:https://www.php.cn/faq/2684019.html
    上一篇在Worker线程间使用getPrototypeOf深度验证对象合规性的方法 下一篇JavaScript任务源管理:不同任务源执行优先级如何配置
    本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

    相关推荐

    补充同频道和同主题内容,方便继续浏览更多相关内容。

    同类最新

    继续查看同栏目最近更新的文章。

    更多
    checked表单属性与CSS变量实现换肤原理
    前端开发 · 2026-07-02

    checked表单属性与CSS变量实现换肤原理

    先聊一个有意思的现象:不需要编写任何 JavaScript,仅靠一个 :checked 伪类,就能驱动整个主题切换系统。听起来很神奇,但原理其实并不复杂——核心在于,:checked 是浏览器原生状态的实时镜像,而不是 JS 模拟出来的开关。 用户点击 ,或者用键盘空格键选中它,状态更新的那一刻,C

    HTML meta标签页面定时跳转实现
    前端开发 · 2026-07-02

    HTML meta标签页面定时跳转实现

    说到前端开发中最简洁的页面跳转方式,meta http-equiv= "refresh " 绝对算得上一个经典方案。不过别看它结构简单,格式上稍有疏忽,页面就可能原地卡死,或者直接跳到一个错误地址。下面把几个最容易踩坑的细节彻底讲清楚,帮你避开这些常见陷阱。 使用 http-equiv= "refresh

    Cypress跨测试用例状态传递的不推荐但可选方案
    前端开发 · 2026-07-02

    Cypress跨测试用例状态传递的不推荐但可选方案

    Cypress 默认的设计哲学很干脆:每个测试用例都必须是独立小王国,谁也不靠谁。这意味着 it() 执行前,浏览器上下文会被“一键还原”——页面状态、LocalStorage、Cookies 统统清空,强制维护测试隔离。这一规则让很多新手头疼:明明前一个测试已经创建了员工,后一个测试怎么就没法直接

    全面深度解析HTML主体main标签唯一性原则与使用规范
    前端开发 · 2026-07-02

    全面深度解析HTML主体main标签唯一性原则与使用规范

    在进行前端无障碍审计时,不少开发者会遇到一个奇怪的场景:浏览器不报错,但Lighthouse却直接标红“duplicate-main”。这其实是语义层与渲染层之间的根本差异。 为什么浏览器不报错但 Lighthouse 直接标红 duplicate-main 关键原因就在于:`main` 是语义锚点

    HTML main标签在文档结构中的唯一性详解
    前端开发 · 2026-07-02

    HTML main标签在文档结构中的唯一性详解

    先做一个快速检测:打开你最近开发的一个页面,按下 Ctrl+F 搜索 。如果搜索结果里出现2个以上,那这篇文章建议你认真读完。 本期要聊的主题,是HTML标签中一个看似简单、实际极易踩坑的核心知识点:main标签的唯一性。很多开发者知道这个标签的存在,但真正写到项目里,尤其是用了React、Vue这