elementui树形表格数据量大时,会造成严重卡顿问题的解决方案

2022-03-25 11:04:02

之所以会造成卡顿,是因为该组件是一次性将所有数据全部渲染,dom数量过于庞大,并且在展开树操作的时候,用了大量递归循环语句,性能受到严重影响,造成卡顿。

我想到的解决方式有两种:

一:分页,让后端添加分页查询,以节点数统计数据条数进行展示。(但实际需求中较少,一般树形表格都是不分页)。

二:将请求到的树形数组数据备份(tableDataCopy),初始化仅仅展示指定层级(如第一层树)的数据,将第一层下面的所有的children全部置为null,并记录存在children的节点,设置hasChild的状态,判断是否存在子节点。这样就不会在初始化页面时展示完所有数据,实现按需加载。

        以上仅仅能满足展示功能,当存在操作节点上移下移/删除/编辑等操作的情况下,展示的节点就不能同步刷新(因为备份数据不能双向数据绑定了)。处理办法:更新节点(两种方式,第一种使用render()来实现,第二种是给表格绑定key(利用diff算法),只要操作了数据,就改变key就能实现节点更新)。

请求数据和展开代码如下:

            // 获取树形列表
            getList(active) {
                this.loading = true;
                let data = Object.assign({}, this.searchForm);
                API.getDepartmentListAll(data)
                    .then((res) => {
                        // this.tableData = res.data;
                        this.expandRow.push(res.data[0].id); //默认展开
                        this.tableDataCopy = res.data || [] // 备份的全量数据
                        if (active) { //是否是操作按钮点击的数据
                            this.tableData = this.commonJs.mapNewTableData(res.data,this.expandRow); //展开已展开的数据
                        } else {
                            this.tableData = this.commonJs.mapNewTableData(res.data,res.data[0].id); //默认展开第一层
                        }
                        this.componentKey += 1; //销毁更新dom
                    }).finally(() => {
                        this.loading = false;
                        this.getTableHeight();
                    })
            },

            // 展开按钮
            getChildrens(tree, treeNode, resolve) {
                let data = Object.assign({}, this.searchForm, {
                    deptId: tree.id
                });
                API.getDepartmentListAll(data)   //传入展开按钮层级节点的id,获取子节点
                    .then((res) => {
                        this.tableDatas = JSON.parse(JSON.stringify(res.data.children)).map(item => { // 展示数据
                            // hasChildren 表示需要展示一个箭头图标
                            item.hasChildren = item.children && item.children.length > 0
                            // 只展示一层也可以配置tree-props里面的children为其他字段
                            item.children = null
                            // 记住层级关系
                            item.idList = [item.id]
                            return item
                        })
                        resolve(this.tableDatas)
                    })
            },

当存在操作按钮,数据更新后,利用上述key更新节点后 ,会将展开的状态初始为初次进度页面的状态,不能停留在操作的节点。解决方法:可以在点击操作按钮的时候,使用findParentNode方法递归查询到该节点所有的祖先节点id并记录(expandRow,该变量也是树形表格:expand-row-keys默认展开的数据),然后再请求刷新数据时,通过mapNewTableData()方法递归处理将对应的祖先节点id对应的children给展示处理,其他的节点还是一样的置为null。代码如下:

    /**
     * 
     * @param 递归,当前节点的祖先节点id数组(包含自身)
     * acceptUnitNodes树状结构数据,ids当前节点的id
     */
    findParentNode(acceptUnitNodes, ids) {
        var parentNodes = [] // 所有父节点
        var forfun = function(id, nodes) {
            for (var i = 0; i < nodes.length; i++) {
                var currentNode = nodes[i]
                if (currentNode.id === id) {
                    return currentNode.id
                } else if (currentNode.children) {
                    var validNodeId = forfun(id, currentNode.children)
                    if (validNodeId && parentNodes.indexOf(validNodeId) < 0) {
                        parentNodes.push(validNodeId)
                    }
                    if (validNodeId) {
                        return currentNode.id
                    }
                }
            }
        }
        var validNodeId = forfun(ids, acceptUnitNodes)
        if (validNodeId && parentNodes.indexOf(validNodeId) < 0) {
            parentNodes.push(validNodeId)
        }
        return parentNodes
    },

    //递归处理数据,当有展开记录的时候,将有展开记录的数据的子节点显示出来
    mapNewTableData(data, pidArr) {
        let dataNew = JSON.parse(JSON.stringify(data));

        function mapNewArr(dataNew, pidArr) {
            dataNew.map(item => {
                if (pidArr.indexOf(item.id) === -1) {
                    item.idList = [item.id]
                    item.hasChildren = true
                    item.children = null
                    // 记住层级关系
                    return item;
                } else {
                    mapNewArr(item.children, pidArr)
                }
            })
            return dataNew;
        }
        mapNewArr(dataNew, pidArr);
        return mapNewArr(dataNew, pidArr);;
    },

操作按钮:

            //上移//下移
            upDownLayer(index, row, type) {
                this.expandRow = (this.commonJs.findParentNode(this.tableDataCopy, row.id)).splice(1); //调工具,获取点击当前节点的祖先节点id数组。
                let loading = Loading.service({
                    target: document.querySelector(".tableBox"),
                });
                API.getShiftOrd({})
                    .then((res) => {
                        this.$message.success(type === 'up' ? '上移成功' : '下移成功');
                        this.getList(true);
                    }).finally(() => {
                        loading.close();
                    })
            },
  • 作者:zx&amp;it
  • 原文链接:https://blog.csdn.net/qq_40639028/article/details/123542268
    更新时间:2022-03-25 11:04:02