虚拟列表之树形结构

今天给大家分享是一个 Tree 组件的虚拟列表,和往常大家经常使用纯一维数据进行的虚拟列表不太一样,但是总体思路大致相同。

背景

公司要做一个展示组织架构信息表,大公司可能有上万个人员,因为存在部门和子部门,是一个树形结构,使用传统的递归写的 Tree 组件加载,页面会直接崩溃。这个时候就需要一个虚拟 Tree 组件的诞生了。

方案

分析现状

我们先回忆一下传统的虚拟列表的方案。

由图片我们可以看到。虚拟列表是通过滚动列表,动态的显示数组中下标从 i 到 j 的数据,以达到部分渲染,减少 dom 数量,从而防止浏览器全量渲染导致的卡顿和崩溃的问题。

那我们能不能让 Tree 组件直接套用这个思路了,当然是可以的。但是如果这个 Tree 组件的层级很深了,每个层级也有上千条数据了,按照之前的思路是不是就不行了。因为在展开节点的时候,子节点数据量很大的话,那会直接全量渲染,导致页面卡顿和崩溃,有人会说我在展开数据后,重新计算该渲染哪些数据了,那不就行了,道理是这样的,那我们下面就介绍该怎么计算获得新的展示数据。

json 复制代码
// 这种大量一维数据结构当然可以
[
    {
        lable: 'xxx',
        value: 1,
    }
    // ...
]


// 如果是这种深度很深的结构,还是找个老思路,可能就没办法了。
[
    {
        lable: 'xxx',
        value: 1,
        children: [
            {
                label: 'xxxx-1',
                value: '1-1',
                children: [
                    {
                        label: 'xxxx-2',
                        value: '2-1',
                    }
                ]
            }
            // ...
        ]
    }
]

解决思路

大致分析
  1. 既然虚拟列表是针对一维的数据结构,那我们是不是可以将 Tree 数据结构拍平成一维的,答案肯定是可以的。
  2. 数据既然是一维的,那是不是可以直接复用传统的虚拟列表方案了。
细化细节

有人会问,Tree 动态拍平成数组,那怎么合理的展示 Tree 的结构了,其实我们可以用 padding-left 来模拟展示树形结构。

  1. 首次加载组件,渲染 Tree 数据结构的外层一维数据结构。
  2. 点击展开子节点时候,动态的将子节点下所有数据,拿出来,拼接在父节点下标 index + 1 上。
  3. 获得新的一维数据,重新计算渲染展示的结束下标。
  4. 周而复始的动态拍平数据,以此维护一个一维数据,虚拟 Tree 组件好像就写完了。
js 复制代码
const nodes = [
    {
        label: 'xxx',
        value: '1',
        children: [
            {
                label: 'xxx-1',
                value: '1-1',
            },
        ]
    },
    
]

 // children 节点的渲染取消传统的递归组件渲染的形式,采用动态的维护 拍平数据 去做展开和收缩
 const flatten = () => {
    const expandedKeys = ['1']
    const flattenNodes = []
   
    function traverse() {
      const stack = []
      for (let i = nodes.length - 1; i >= 0; --i) {
        stack.push(nodes[i])
      }
      while (stack.length) {
        const node = stack.pop()
        if (!node) continue
        flattenNodes.push(node)

        // 只渲染展示的节点
        if (expandedKeys.has(node.key)) {
          const children = node.children
          if (children) {
            const length = children.length
            for (let i = length - 1; i >= 0; --i) {
              stack.push(children[i])
            }
          }
        }
      }
    }
    traverse()
    return flattenNodes
  }

参考资料

Element Plus: github.com/element-plu...

相关推荐
恋猫de小郭1 小时前
Flutter Zero 是什么?它的出现有什么意义?为什么你需要了解下?
android·前端·flutter
崔庆才丨静觅7 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60618 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了8 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅8 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅9 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅9 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment9 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅9 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊9 小时前
jwt介绍
前端