element-ui el-tree大数据量勾选节点卡顿问题

4,955 阅读2分钟

前言

Element-UI el-tree 组件在大数据量的情况下进行编辑回显,会导致界面卡顿。为了解决这一问题,需要对el-tree组件的编辑回显逻辑进行优化。本文将探讨如何在大数据量的情况下优化el-tree组件的勾选回显性能。

问题分析

以下是模拟对5000+条数据的编辑回显。 checked动画.gif

回显勾选耗时: 2285.337890625 ms

在回显勾选时,使用setChecked设置勾选,每次都会触发源码中的getChildState函数。该函数会循环改变子节点状态,并循环递归父级及父级下的子节点,主要是为了判断子节点是否还有勾选的状态,以判断父级或祖级设置半勾选还是未选中状态。每次子节点状态改变时,都会执行上述判断逻辑。

当循环去勾选节点时,就相当于执行了,5000+次循环递归。导致循环次数指数级增长。

具体原因在element-ui el-tree大数据量取消勾选卡顿问题文章中有提及

解决方案

el-tree组件提供了check-strictly属性,该属性可以实现父子节点不关联。

在回显勾选时,只需要在开始勾选前对check-strictly设为true,在最后一个节点勾选前设为false,让组件恢复成联动状态。分析起来是很简单,但真操作起来,还是有点繁琐的。

首先,check-strictly属性,是用于TreeStore类,且TreeStore只初始化一次,所以动态改变check-strictly是无效的。其次,想要动态改变check-strictly,只能通过使用$refs,获取组件实例来进行修改。

tree.vue 源码如下:

屏幕截图 2024-05-14 163339.png

tree.vue 源码文件路径:packeages\tree\src\tree.vue

可以使用this.$refs.xxx.store.checkStrictly,强制修改checkStrictly

PS:如果是懒加载el-tree,使用this.$refs.xxx.checkStrictly修改参数。

使用checkStrictly效果预览:

checked1动画.gif 5000+回显勾选,效率提升99%

回显勾选耗时: 8.691162109375 ms

源码

<template>
  <div>
    <button @click="setCheck">勾选</button>
    <el-tree
        ref="tree"
        height="600px"
        :data="data"
        @check="check"
        show-checkbox
        node-key="id"
        :default-expanded-keys="[]"
        :props="defaultProps">
    </el-tree>
  </div>
</template>

<script>
export default {
  data() {
    return {
      data: [{
        id: 1,
        label: '一级 1',
        children: [{
          id: 4,
          label: '二级 1-1',
          children: [{
            id: 9,
            label: '三级 1-1-1'
          }, {
            id: 10,
            label: '三级 1-1-2'
          }]
        }, {
          id: 99,
          label: 'asasa'
        },
          {
            id: 88,
            label: 'vcvcvc'
          }]
      }, {
        id: 2,
        label: '一级 2',
        children: [{
          id: 5,
          label: '二级 2-1'
        }, {
          id: 6,
          label: '二级 2-2'
        }]
      }, {
        id: 3,
        label: '一级 3',
        children: [{
          id: 7,
          label: '二级 3-1'
        }, {
          id: 8,
          label: '二级 3-2'
        }]
      }],
      defaultProps: {
        children: 'children',
        label: 'label'
      },
      ids: [10, 8]
    };
  },
  created() {
    for (let i = 0; i < 5000; i++) {
      this.ids.push(i + 100)
      this.data[0].children.push({
        id: i + 100,
        label: i
      })
    }
  },
  methods: {
    /**
     * 勾选
     **/
    setCheck() {
      console.time('勾选耗时')
      let node = null
      let obj = {};
      let key = null
      this.$refs.tree.store.checkStrictly = true; // 父子节点不关联

      this.ids.map(item => {
        node = this.$refs.tree.getNode(item)

        key = node.parent.key || node.key

        this.$refs.tree.setChecked(item, true, true)

        // 用于,触发勾选函数,实现级联勾选
        if (!obj[key]) {
          obj[key] = node.data;
        }
      })

      this.$refs.tree.store.checkStrictly = false;  // 父子节点关联

      // 触发勾选函数,实现级联勾选
      this.$nextTick(() => {
        for (let key in obj) {
          this.$refs.tree.setChecked(obj[key], true);
        }
      });

      console.timeEnd('勾选耗时')
    },

    check(a) {
      let node = this.$refs.tree.getNode(a.id)
    }
  }
};
</script>