阅读源码:tree-iterate

140 阅读3分钟

github.com/arupex/Tree…

最近做一些树形的操作,发现有个轻量的库,就不用自己写了。使用后看下源码,先看下用法和整体策略。

1. 基础用法

const each = require('tree-interate')

const tree = []

const options = {
  ignoreParentalsArray: false,
  ignoreParents: false,
}

each(tree, options, function interate (node, parent, parents, parentalIndex) {
  const parentNames = []

  // 可以很方便的找到当前节点所有的父亲
  parents.forEach(function(anscestor){
    parentNames.push(anscestor.name)
  })

  console.log(
    'Current Node :', node.name,
    'Current Parent : ', parent.name,
    'Parents : [ ', parentNames , ' ]'
  )
})

2. 整体策略

  1. 创建一个内置的 Root 节点,这个主要用于适配要遍历的 tree 结构
const itOver = [
  {
    child: {
      __root: true,
      // tree 就是传入需要遍历的树
      children: tree,
    }
  }
]

// 根据参数做一些根节点的初始化,所以全部属性如下
const itOver = [
  {
    child: {
      __root: true,
      // tree 就是传入需要遍历的树
      children: tree,
      // 父级节点,有多种判断,这里其实判断得很怪,感觉一定不会执行分支逻辑
      parent: itOver,
      // 以下 2 个属性根据配置决定是否需要,parents 会存所有路径的父节点数据,很占内存
      parents: [],
      // 这个属性感觉只是一个遍历的当前父的索引记录,在个别场景估计才有用
      parentalIndex: [],
    }
  }
]

// 根节点 parent 的初始化逻辑
// 先定义 parent 就是自身
// { child, parent: { child } }
itOver[0].parent = itOver;

// 如果 parent 这个时候不存在(这个判断没有用,默认肯定有值),或者是数组为空
// itOver[0].parent.length -> 直接初始化的 itOver.length,肯定等于 1
// false || false -> false
if (!itOver[0].parent || itOver[0].parent.length === 0) {
  // 就把当前 parent = { child }
  itOver[0].parent = itOver[0];
}

// 如果没有 children 且 child 不为空,这里感觉也不存在,因为在最初的时候 !tree 直接 return 了代码执行
// itOver[0].parent.child -> 这个值是初始化直接给的,所以一定存在
// false && true -> false
if (!itOver[0].parent.children && itOver[0].parent.child) {
  itOver[0].parent = itOver[0].parent.child;
}

创建此结构可以适配 2 种入参,可以很好的从上到下的遍历

// 第一种结构, 直接就是铺开的多根节点
const tree1 = [
  {
    children: [],
  },
  {
    children: [],
  },
]

// 第二种结构,常规的一个树对象
const tree2 = {
  children: [],
}
  1. 对树进行遍历

这里使用的深度优先遍历,从根节点开始,遍历所有子节点,从左到右跟踪记录。退出条件有 2 个:itOver 数组为空,或者 val.child 为空。所以这里其实有漏洞,[undefined] 类型的子节点会影响到遍历是否继续。

let val

while (val = itOver.shift()) {
  if (val.child) {
    // 获取当前 Context 下的节点信息,给到回调遍历函数进行操作

    // 判断是否是根节点
    const parentIsRoot = val.parent && val.parent.__root

    const callbackResponse =
      // 根节点
      interatorCallback(val.child, {}, [], val.parentalindex)
      // 非根节点
      interatorCallback(val.child, val.parent, val.parents, val.parentalIndex)

    // 判断遍历是否有返回值,如果没有返回值,会继续遍历
    // 这里有隐式判断,感觉可以直接判断 undefined and null 会更加合适一些
    if (!callbackResponse && callbackResponse !== undefined) {
      return tree
    }

    // 遍历树所有的子节点,从左到右
    if (val.child.children) {
      // parentalsIndex 主要记录当前遍历到的父级索引
      let parentalIndex

      // zi 表示当前节点子节点的索引
      for (let zi = 0; zi < val.child.children.length; ++zi) {
        parentalIndex = [zi]

        for(let z = 0; z < val.parentalIndex.length; ++z){
          parentalIndex.push(val.parentalIndex[z])
        }

        // 主要逻辑
        itOver.push({
          parent: val.child,
          child: val.child.children[zi],
          parents: [val.child].concat(val.parents),
          parentalIndex: parentalIndex,
        })
      }
    }
  }
}

return tree