展示大量数据节点(tree),引发的一次性能排查

798 阅读5分钟

功能需求,左边是一个无限节点的嵌套树结构,节点可能会有几万个,这时候直接展示是不现实的,所以即需要使用虚拟列表又需要进行懒加载节点数据。

image.png

我们的项目整体的组件库是element-plus,所以就很容易想着去使用element-plustree, tree-v2组件,但是tree组件只支持动态加载节点,不支持树的虚拟列表。而tree-v2只支持获取全部树数据然后进行虚拟列表展示。

发现这两个功能不能同时使用,但是我们项目的需求并不能去获取全部树结构,会很慢,并且数据是逐渐写入的。

于是就查找其他相关的tree组件,发现市面上的组件都是只支持动态加载或者拿到全部数据做虚拟列表展示。于是就分析了一下element-plus tree-v2源码,发现并不好扩展。就想着自己去实现动态加载后的树结构虚拟化,就阅读了下面这些文章。

在某个星期天的中午醒来想着去看看ant-design组件库有没有对应的组件,发现他刚好满足我的需求,于是就去查看ant-design-vue中的tree,于是就发现了新大陆, 将组件替换成了ant-design-vue

在使用element-plus, ant-design-vue中的tree发现他们的设计理念完全不同。前者更多的是暴露出方法让你自己去完成你的逻辑或者提供一个属性配置,后者更多的使用双向绑定,所有的操作都是对数据响应式的操作。比如手风琴模式。前者就直接配置属性,后者就需要自己实现逻辑。

废话不多说,本以为使用完这个组件就解决了自己的需求。可是对于数据量过大的节点来说,我们的交互可能导致数据加载过慢。

下面我们来看下大数据量和小数据量请求csv所花费的时间,发现大数据量第一次请求csv所花费的时间基本都是1s左右,小数据量第一次请求csv所花费的时间都是几十毫秒。

大数据量.gif

小数据量.gif 看了查询时间,以为是后端接口获取数据太慢,后端那边看了一下查询时间发现只有几十毫秒,可是我这边获取数据却需要几百毫秒,并且节点树的数据量不同响应csv数据的时间也不同。

啊,这什么操作。自己陷入了沉思,一顿分析还是没有发现具体的原因。本来以为是数据量过大(其实也才几十M)导致请求数据过慢,可是这也不是具体原因啊。最后我们组长提醒我让我直接睡眠100ms,看看时间是多少。

const getGroupCsvListData = async (
  groupItem: PageNodeData
) => {
  await new Promise<void>(resolve => {
    setTimeout(() => {
      console.log("睡眠100ms")
      resolve()
    }, 100)
  })
};

我们再来看看具体的时间

大数据量睡眠100ms.gif

小数据量睡眠100ms.gif

发现大数据量请求依旧是1s左右,小数据量是100ms左右。这就彻彻底底说明不是后端响应过慢,就是前端页面的锅,那为啥自己请求时也没做大数据量计算,怎么就这么慢呢。

并且只是触发csv节点请求csv数据时,它又比较快。 image.png

image.png

到这里就可以确定是ant-design-vue的锅了。组件内部在每次加载树节点肯定做了大量计算才导致请求这么慢的。

那我们就来使用devtools performance来分析一下,具体看看是啥原因。

image.png

image.png

我们点击去具体的文件发现, 这里

image.png

到这里我们就完整分析出来具体的原因了。就看看后续如何进行优化了。

这里说一下在后端看来,我这种根本不叫虚拟列表,他认为虚拟列表就是一次加载指定条的数据,而不是一次性把当前节点的数据都拿到。但是对于这种tree组件来说这种方式并不适合。它是对整棵树进行虚拟化,而不是对单独节点进行虚拟化。并且他是把所有的节点都扁平化进行展示的,并没有嵌套关系。如果对单独节点进行虚拟化,会出现很多滚动条,并且页不好处理数据再次请求的条件,并且随着数据的加载,依旧会出现这种上面的那种问题。

大佬们有啥好的想法,这里交流一下呗。

往期年度总结

往期文章

专栏文章