posts - 9,comments - 1,views - 11941
复制代码
 let arr = [
      {
        id: '1',
        title: '节点1',
        children: [
          {
            id: '1-1',
            title: '节点1-1'
          },
          {
            id: '1-2',
            title: '节点1-2'
          }
        ]
      },
      {
        id: '2',
        title: '节点2',
        children: [
          {
            id: '2-1',
            title: '节点2-1'
          }
        ]
      }
    ]

    /**
     广度优先遍历
    **/

    function breadthFisrstSearch(tree, cb) {
      let node = null
      let list = [...tree]
      while (node = list.shift()) {
        cb(node)
        node.children && list.push(...node.children)
      }
    }

    breadthFisrstSearch(arr, (data) => {
      data.text = data.id.toString() + data.title
      console.log(data)
    })

    /**
    
    ========================

      深度优先遍历

    ========================

    **/


    /**
      先序遍历
    **/
    function depthFirstTraversal(tree, cb) {
      tree.forEach(item => {
        cb(item)
        item.children && depthFirstTraversal(item.children, cb)
      })
    }

    /**
     后序遍历
    **/

    function depthFirstTraversalEnd(tree, cb) {
      tree.forEach(item => {
        item.children && depthFirstTraversalEnd(item.children, cb)
        cb(item)
      })
    }

    /**
     先序遍历 循环版
    **/

    function depthFirstTraversalWithLoop(tree, cb) {
      let node = null
      let list = [...tree]
      while (node = list.shift()) {
        cb(node)
        node.children && list.unshift(...node.children)
      }
    }

    /**
     后序遍历 循环版
    **/

    function depthFirstTraversalEndWithLoop(tree, cb) {
      let node = null
      let list = [...tree]
      let i = 0
      while (node = list[i]) {
        let len = node.children ? node.children.length : 0
        if (!len || list[i - 1] === node.children[len - 1]) {
          cb(node)
          i++
        } else {
          list.splice(i, 0, ...node.children)
        }
      }
    }


    /**
     数据过滤
    **/

    function treeFilter(tree, func) {
      // 使用map复制一下节点,避免修改到原树
      return tree.map(node => ({ ...node })).filter(node => {
        node.children = node.children && treeFilter(node.children, func)
        return func(node) || (node.children && node.children.length)
      })
    }


    /**
    数据查找
    **/

    function treeFind(tree, cb) {
      for (const data of tree) {
        if (cb(data)) return data
        if (data.children) {
          const res = treeFind(data.children, cb)
          if (res) return res
        }
      }
      return null
    }

    /**
    节点路径
    **/


    function treeFindPath(tree, cb, path = []) {
      if (!tree) return []
      for (const data of tree) {
        path.push(data.id)
        if (cb(data)) return path
        if (data.children) {
          const findChildren = treeFindPath(data.children, cb, path)
          if (findChildren.length) return findChildren
        }
        path.pop()
      }
      return []
    }
复制代码
复制代码
 let list = [
      {
        id: '1',
        title: '节点1',
        parentId: '',
      },
      {
        id: '1-1',
        title: '节点1-1',
        parentId: '1'
      },
      {
        id: '1-2',
        title: '节点1-2',
        parentId: '1'
      },
      {
        id: '2',
        title: '节点2',
        parentId: ''
      },
      {
        id: '2-1',
        title: '节点2-1',
        parentId: '2'
      }
    ]
    function ArrayToTree(arr) {
      let tree = []
      let map = {}
      arr.forEach(item => {
        item.children = []
        map[item.id] = item
      })
      arr.forEach(item => {
        let parent = map[item.parentId]
        if (parent) {
          parent.children.push(item)
        } else {
          tree.push(item)
        }
      })
      return tree
    }

    function arrayToTreeWithReduce(arr) {
      let map = arr.reduce((map, item) => {
        item.children = []
        map[item.id] = item
        return map
      }, {})
      return arr.filter(item => {
        map[item.parentId] && map[item.parentId].children.push(item)
        return !item.parentId
      })
    }

  //递归实现
  function treeToList(tree, result = [], level = 0) {
    tree.forEach(node => {
      result.push(node)
      node.level = level + 1
      node.children && treeToList(node.children, result, level + 1)
    })
    return result
  }

  // 循环实现
  function treeToList(tree) {
    let node, result = tree.map(node => (node.level = 1, node))
    for (let i = 0; i < result.length; i++) {
      if (!result[i].children) continue
      let list = result[i].children.map(node => (node.level = result[i].level + 1, node))
      result.splice(i + 1, 0, ...list)
    }
    return result
  }
复制代码

 数据结构

复制代码
data = [
    {
        "id": 1,
        "pid": 0,
        "font_route": "",
        "api_action": "",
        "is_menu": 1,
        "is_show": 0,
        "title": "数据统计",
        "children": [
            {
                "id": 2,
                "pid": 1,
                "font_route": "/main/ActivityList",
                "api_action": "Index/getActivityList",
                "is_menu": 1,
                "is_show": 1,
                "title": "文章列表",
                "children": [
                    {
                        "id": 6,
                        "pid": 2,
                        "font_route": "/main/ArticleHot",
                        "api_action": "Index/getArticleHot",
                        "is_menu": 0,
                        "is_show": 1,
                        "title": "文章热度统计"
                    }
                ]
            }
        ]
    }
    
 ]
复制代码

那么有以下几种方案可以实现:

1.刷新页面
2.再次向服务器发起请求,更新数据
3.直接更改此次列表数据,vue中会自动刷新视图
第一、第二种方案都需要再次发送请求与服务端交互,比较浪费带宽与效率。这里重点说下第三种直接修改本次列表数据的方法。

直接修改树状结构的话,常用的方法就是递归操作,通过递归实现树状结构的新增、删除、查找。

新增对象
直接上基础代码:【以id作为关键词,children作为下级字段】

复制代码
const handleData = (id, data, obj) => {
  data.forEach(item => {
    if (item.id === id) {
      item.children ? item.children.push(obj) : item.children = [obj]
    } else {
      if (item.children) {
        handleData(id, item.children, obj)
      }
    }
  })
  return data
}
复制代码

改写可以自定义关键词、自定义下级字段的函数:

复制代码
function insertTreeListItem (treeList, key , item , childField ,keyField) {
 
    var childField = arguments[3] ? arguments[3] : 'children'
    var keyField = arguments[4] ? arguments[4] : 'id'
 
    treeList.forEach(treeitem => {
      if (treeitem[keyField] === key) {
        treeitem[childField] ? treeitem[childField].push(item) : treeitem[childField] = [item]
      } else {
        if (treeitem[childField]) {
            insertTreeListItem(treeitem[childField], key , item , childField ,keyField)
        }
      }
    })
    return treeList
}
复制代码

使用vue的请注意:

在vue中如果直接使用以上代码修改原数据的话,那么你会发现数据虽然更新了,但是有时候视图并没有更新。细心的小伙伴应该很快发现了,其中有对象的直接赋值语句。

总所周知,在vue2中直接的对象赋值时无法更新视图的。在本案例中由于是公共函数,也不可能在函数中直接使用this.$set赋值。解决方案有两个:

1、让后台为每一个item都加上children的子项目,这样前端就不用使用对象赋值了。
2、前端不直接传入data的值,通过深拷贝得到一个数据,再重新赋值回data

这里推荐使用第二种方法:

 删除对象

删除对象写起来比较简单,本质上也是递归。删除可以不用返回新数据,直接影响原数组也问题不打,需要返回新对象的可以直接改写一下。

基础函数:

复制代码
function removeTreeListItem(treeList, id) { // 根据id属性从数组(树结构)中移除元素
  if (!treeList || !treeList.length) {
    return
  }
  for (let i = 0; i < treeList.length; i++) {
    if (treeList[i].id === id) {
      treeList.splice(i, 1);
      break;
    }
    removeTreeListItem(treeList[i].children, id)
  }
}
复制代码

改写成可以自定义关键词、自定义下级字段的函数

复制代码
function removeTreeListItem(treeList, key , childField ,keyField) { 
 
    var childField = arguments[2] ? arguments[2] : 'children'
    var keyField = arguments[3] ? arguments[3] : 'id'
 
    if (!treeList || !treeList.length) {
      return
    }
    for (let i = 0; i < treeList.length; i++) {
      if (treeList[i][keyField] === key) {
        treeList.splice(i, 1);
        break;
      }
      removeTreeListItem(treeList[i][childField], key ,childField ,keyField)
    }
}
复制代码

查找对象

基础函数:

复制代码
getObjByTree = (data, id) => {
        let result = null
        if (!data) return // return; 中断执行
        for (let i in data) {
            if (result !== null) break
            let item = data[i];
            if (item.id=== id) {
                result = item;
                break;
            } else if (!!item?.children?.length) {
                result = this.getObjByTree(item.children, id);
            }
        }
        return result;
    }
复制代码

改写成可以自定义关键词、自定义下级字段的函数

 

复制代码
function searchTree(treeList, key , childField ,keyField){
 
    var childField = arguments[2] ? arguments[2] : 'children'
    var keyField = arguments[3] ? arguments[3] : 'id'
 
    let result = null
    if (!treeList) return // return; 中断执行
    for (let i in treeList) {
        if (result !== null) break
        let item = treeList[i];
        if (item[keyField]=== key) {
            result = item;
            break;
        } else if ( item[childField] && item[childField].length>0 ) {
            result = searchTree(item[childField], key);
        }
    }
    return result;
}
复制代码

 

以上三个函数,应该可以解决大部分的树状结构数据相关的业务代码

 

posted on   James__xu  阅读(757)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· 葡萄城 AI 搜索升级:DeepSeek 加持,客户体验更智能
· 什么是nginx的强缓存和协商缓存
· 一文读懂知识蒸馏
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

点击右上角即可分享
微信分享提示