优化实战 第 33 期 - 使用 while 循环把树结构拍平

2,271 阅读2分钟

很多同学的第一想法是通过递归来实现,如果你也是这样的想法,请阅读 第08期 - 遍历一次把扁平数据结构转Tree 一文,其中有详细的解说递归存在的性能问题,所以不推荐使用递归来实现

数据结构

  • 树形数据
    [
      { 
        id: 1, pid: null, name: 'M1部门', children: [
          { id: 11, pid: 1, name: '张三', },
          { id: 12, pid: 1, name: '李四' },
          { id: 13, pid: 1, name: '王五' }
        ]
      },
      {
        id: 2, pid: null, name: 'M2部门', children: [
          { id: 21, pid: 2, name: '赵六' },
          { id: 22, pid: 2, name: '周七' },
          { id: 23, pid: 2, name: '吴八' }
        ]
      }
    ]
    
  • 扁平数据
    [
      { id1pidnullname'M1部门' },
      { id11pid1name'张三' },
      { id12pid1name'李四' },
      { id13pid1name'王五' },
      { id2pidnullname'M2部门' },
      { id21pid2name'赵六' },
      { id22pid2name'周七' },
      { id23pid2name'吴八' }
    ]
    

优秀源码分析

  • 将模板内容存入文档片段

    const node2fragment = node => {
      // 创建文档片段
      const fragment = document.createDocumentFragment()
      let child = null
      // 遍历传入节点中的所有子节点,依次添加到文档片段中
      while(child = node.firstChild) {
        fragment.appendChild(child)
      }
      return fragment
    }
    
  • 文档片段

    文档片段存储由 DOM 节点(nodes)组成的文档结构,它并不是真实 DOM 树的一部分,而是存在于 内存 中。所以它的变化不会触发 DOM 树的重新渲染,通常会带来更好的性能

  • Node.appendChild() 方法

    将一个节点附加到指定父节点的末尾处。如果将被插入的节点已经存在于当前文档的文档树中,那么它将从原先的位置移动到新的位置(不需要事先移除要移动的节点)

循环拍平树结构

  • 思路方案(借鉴优秀源码)

    通过 while 循环将数据源 source 中的每一项依次添加到目标数据 list

    通过解构取出循环项 item 中的子级,以前置队列的方式放至 source 中,直到数据全部取完

  • 参数注解

    /**
    * 扁平化树结构
    * 
    * @param {Array} source 源数据
    * @param {String} branch 树杈字段名称
    * @param {Boolean} isChildren 是否包含子树数据,默认不包含
    * @return {Array} list 目标数据
    * @example
    *
    *   flatTree(data)
    *   flatTree(data, 'children', true)
    */
    
  • 方法封装

    const flatTree = (source, branch = 'children', isChildren = false) => {
      let list = [], item = {}
      while (item = source.shift()) {
        const { [branch]: children, ...rest } = item
        list.push(isChildren ? item : rest)
        if (children?.length) {
          source = [...children, ...source]
        }
      }
      return list
    }
    

    一起学习,加群交流看 沸点