自学内容网 自学内容网

关于Vue中涉及到大量数据的级联菜单树状结构的数据多选勾选页面卡顿卡死问题

项目场景:如题

提示:有个需求,级联菜单树状结构的数据高达3万多条,多选,只需要最后一层级value

原因分析:页面一下子渲染大量数据会导致浏览器内存暴涨100%,导致页面卡死,而且element的el-cascader会监听每项,更是卡死

解决方案:

提示:放弃el-cascader,改用select + tree来实现,放弃element,改用原生tree配合el-select

原生tre引用依赖了vue-tree:

GitHub - wsfe/vue-tree at 2.x(vue2.x)

https://github.com/wsfe/vue-tree (vue3.x)

本人使用的vue2.x,查看文档属性方法,灵活更加需求写逻辑,目标是不卡死页面

具体实现:

// template
<template>
<!--多选-->
<el-select
v-model="showlabel"
placeholder="请选择"
:popper-append-to-body="true"
multiple
collapse-tags
clearable
class="tree-cascader"
popper-class="virtual-cascader-warp"
@change="handleShowLabelChange"
@visible-change="handleShowLabelVisible"
@clear="handleClearCascader"
>
<el-option value="" style="height: auto">
<CTree
ref="ctreeRef"
v-model="formData.value"
:data="modelOptions"
:nodeClassName="node => handleNodeClassName(node)"
checkable
clearable
titleField="label"
keyField="value"
ignore-mode="parents"
:cascade="true"
@checked-change="handleCheckTreeChange"
@expand="handleCheckTreeChange"
:style="{ height: '290px' }"
></CTree>
</el-option>
</el-select>
</tempalte>
// script
<script>
// 引入组件
import CTree from '@wsfe/ctree';

// 菜单树状结构通过最后一层id的数组(findPathsByIdArr)获取该id的链式名称, lable 和 value
import { findPathById, findPathsByIdArr } from '@/utils'
// 获取所有的数据列表
import { allSkuTreeApi } from "@/api/activity";

export default {
components: {
        CTree,
    },
data() {
return {
            showlabel: [], // 仅用于select展示回显用
            modelOptions: [],
}
},
methods: {
// ctree - 回显,通过最后一层ID搜索全链路名称
        handleShowLabel() {
            if (this.showlabel.length) {
                this.showlabel = findPathsByIdArr(this.modelOptions, this.showlabel)
            }
        },

        // ctree - 给每个层级添加class,便于自定义样式
        handleNodeClassName(node) {
            return `node-${node._level}`
        },

        // ctree - 打开树状结构、勾选、取消
        handleCheckTreeChange(checkNode) {
            if (checkNode.length) {
                let arrLabel = [];
                checkNode.forEach((item, index) => {
                    if (!item?.children?.length) {
                        arrLabel.push(item.label);
                    }
                });
                this.showlabel = arrLabel;
            } else {
                if (!this.formData.value.length) {
                    setTimeout(() => {
                        this.showlabel = [];
                    }, 10);
                }
            }
        },

        // ctree - select 全部清空,联动去掉勾选
        handleClearCascader() {
            this.$refs['ctreeRef'].clearChecked();
        },

        // ctree - select选择
        handleShowLabelChange() {
            // 过滤 - 空数据,仅展示用
            this.showlabel = this.showlabel.filter((item) => item !== '');
        },

        // ctree - select展开收起
        handleShowLabelVisible(val) {
          if (!val) {
              // 收起时,ctree折叠所有,为了优化下拉必须把勾选的全部展示
              this.$refs['ctreeRef'].setExpandAll(false)
          }
        },

        // 获取数据列表
        async handleAllSkuTree() {
            const { errCode, data } = await allSkuTreeApi(); 
            if (errCode === 0) {
                this.modelOptions = Object.values(data);
            }
        },
}
}
</script>

<style lang="scss" scoped>
    // ctree
    .tree-cascader {
        ::v-deep {
            // 层级多的话,尽量把选择框宽度弄大点
            .el-input {
                width: 700px!important;
            }
            // 隐藏、删除图标,多选都折叠了,不单个删除,如果需求需要另加单个删除逻辑,这里我直接用样式隐藏了单个删除按钮
            .el-tag.el-tag--info .el-tag__close {
                display: none;
            }
        }
    }
}
</style>
<style lang="scss">
// cTree组件样式
@import '~@wsfe/ctree/dist/ctree.css';

.virtual-cascader-warp {
    height: 300px;
    //ctree
    // 设置滚动样式,高度一致
    & > .el-scrollbar > .el-select-dropdown__wrap {
        max-height: 336px;
        margin:0!important;
        .el-scrollbar__view {
            padding: 0;
        }
        .el-select-dropdown__item {
            padding-right: 0;
            padding-left: 10px;
        }
    }
    // select li的不需要变色
    &.el-select-dropdown.is-multiple .el-select-dropdown__item.selected {
        color:#606266;
    }
    .ctree-tree__wrapper  {
        .ctree-tree__scroll-area {
            & > .ctree-tree__block-area {
                & > .node-0,  > .node-1 {
                    .ctree-tree-node__node-body {
                        // 这里是需求不需要前面两级勾选,减少卡顿,故而禁止点击勾选框来勾选、全选,可以不加,更加需求灵活变动
                        & > .ctree-tree-node__square:nth-child(2) {
                            //display: none;
                            pointer-events: none;
                            opacity: 0.4;
                        }
                        // 这里是需求不需要前面两级勾选,减少卡顿,禁止点击文本来勾选、全选,可以不加,更加需求灵活变动
                        & > .ctree-tree-node__title {
                            pointer-events: none;
                        }
                    }
                }
            }
        }
    }
}
</style>
// 另外附上,utils中的方法


/**
 * 菜单树状结构通过最后一层id获取该id的链式名称, lable 和 value
 * @param treeData 菜单树状结构
 * @param targetVal 最后一层ID
 * @param path 链式名称
 * @returns {*|null|*[]}
 */
export function findPathById(treeData, targetVal, path = []) {
    for (let node of treeData) {
        if (node.value === targetVal) {
            return [...path, node.label].join('/');
        }
        if (node.children && node.children.length > 0) {
            let result = findPathById(node.children, targetVal, [...path, node.label]);
            if (result) {
                return result;
            }
        }
    }
    return null;
}

/**
 * 菜单树状结构通过最后一层id的数组获取该id的链式名称, lable 和 value
 * @param treeData 菜单树状结构
 * @param targetValArr 最后一层ID数组
 * @param paths 链式名称
 * @param currentPath 当前链式名称
 * @returns {*|null|*[]}
 */
export function findPathsByIdArr(treeData, targetValArr, currentPath = [], paths = {}) {
    for (let node of treeData) {
        const path = [...currentPath, node.label];
        if (targetValArr.includes(node.value)) {
            paths[node.value] = path.join('/');
        }
        if (node.children && node.children.length > 0) {
            findPathsByIdArr(node.children, targetValArr, path, paths);
        }
    }
    return paths ? Object.values(paths) : {};
}


原文地址:https://blog.csdn.net/qq_38828542/article/details/140489877

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!