很多同学的第一想法是通过递归来实现,如果你也是这样的想法,请阅读 第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: '吴八' } ] } ]
- 扁平数据
[ { id: 1, pid: null, name: 'M1部门' }, { id: 11, pid: 1, name: '张三' }, { id: 12, pid: 1, name: '李四' }, { id: 13, pid: 1, name: '王五' }, { id: 2, pid: null, name: 'M2部门' }, { id: 21, pid: 2, name: '赵六' }, { id: 22, pid: 2, name: '周七' }, { id: 23, pid: 2, name: '吴八' } ]
优秀源码分析
-
将模板内容存入文档片段
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 }
一起学习,加群交流看 沸点