15.树形虚拟列表实现(支持10000+以上的数据)el-tree(1万+数据页面卡死)

news2025/2/27 7:32:47

1.问题使用el-tree渲染的树形结构,当数据超过一万条以上的时候页面卡死

2.解决方法:

使用vue-easy-tree来实现树形虚拟列表,注意:vue-easy-tree需要设置高度

3.代码如下

<template>
    <div class="ve-tree" style="height:calc(100vh - 20px)">
    <!-- 不使用虚拟滚动时只需去掉height参数即可 -->
      <vue-easy-tree
        ref="veTree"
        node-key="id"
        show-checkbox
        height="calc(100vh - 20px)"
        :data="treeData"
        :props="props"
      ></vue-easy-tree>
    </div>
  </template>
   
  <script>
  
import VueEasyTree from "@wchbrad/vue-easy-tree";
// 样式文件,可以根据需要自定义样式或主题(使用这个样式需要装sass-loader以及node-sass)
import "@wchbrad/vue-easy-tree/src/assets/index.scss"

  export default {
    components: {
        VueEasyTree
    },
    data() {
      return {
        props: {
          label: "name",
          children: "children"
        },
        treeData: []
      };
    },
   
    created() {
      const data = [],
        root = 8,
        children = 3,
        base = 1000;
      for (let i = 0; i < root; i++) {
        data.push({
          id: `${i}`,
          name: `test-${i}`,
          children: []
        });
        for (let j = 0; j < children; j++) {
          data[i].children.push({
            id: `${i}-${j}`,
            name: `test-${i}-${j}`,
            children: []
          });
          for (let k = 0; k < base; k++) {
            data[i].children[j].children.push({
              id: `${i}-${j}-${k}`,
              name: `test-${i}-${j}-${k}`
            });
          }
        }
      }
      this.treeData = data;
    }
  };
  </script>

4. 使用方法,首先安装依赖

yarn add @wchbrad/vue-easy-tree

如果不引入样式文件可以不安装(sass-loader以及node-sass)
node-sass:4.14.1
sass-loader:8.0.2
(自己安装的时候失败了,所以选择不引入样式文件)

5.组件引入

import VueEasyTree from "@wchbrad/vue-easy-tree";
// 样式文件,可以根据需要自定义样式或主题
import "@wchbrad/vue-easy-tree/src/assets/index.scss"
 
export default {
  components: {
    VueEasyTree
  }
}

6.功能列表

 1.大数据量支持虚拟滚动
 2.基本树形数据的展示
 3.支持checkbox选择
 4.支持懒加载
 5.默认展开和默认选中
 6.禁用节点
 7.通过多种方式选中节点和获取选中的节点信息
 8.支持自定义节点内容
 9.支持节点过滤
 10.非虚拟滚动下,支持手风琴模式
 11.非懒加载时,支持节点拖拽

支持与element-ui完全相同的主题样式更换,提供与element-ui相同的图标供选用
如果使用element-ui的默认属性代码为

<template>
    <div class="tree-comp">
        <div class="input-box">
            <el-input size="mini" suffix-icon="el-icon-search" clearable v-model="filterInputValue"
                @change="onFilter" placeholder="请输入检索内容">
            </el-input>
        </div>

        <div class="ve-tree" style="height:520px">
            <!-- 不使用虚拟滚动时只需去掉height参数即可 -->
            <vue-easy-tree
                v-for="(treeItem, index) in treeOption" :key="index"
                ref="treeComp"
                node-key="id"
                show-checkbox
                height="520px"
                :data="treeItem.treeData"
                :props="props"
                :filter-node-method="filterNode"
                :highlight-current="true"
                :default-checked-keys="allNodeIds"
                :default-expanded-keys="defaultExpandedKeys[index]"
                :check-on-click-node="true"
                v-bind="treeItem.defaultProps"
                v-on="treeItem.defaultActions"
                @check-change="handleCheckChange"
            ></vue-easy-tree>
        </div>
    </div>
</template>

<script>
import VueEasyTree from "@wchbrad/vue-easy-tree";
const debounce = function debounce(fn, delay) {
    let timer = null;
    return function () {
        clearTimeout(timer);
        let args = arguments;
        let that = this;
        timer = setTimeout(function () {
            fn.apply(that, args);
        }, delay);
    };
};
export default {
    name: 'TreeComp',
    props: {
        treeOption: { type: Array },
        selection: {
            type: String,
            default: 'multiple'
        }
    },
    components: {
        VueEasyTree
    },
    data() {
        return {
            filterInputValue: '',  // 过滤搜素值
            curChoosedData: {} ,    // 当前节点数据
            filterParentNodeId: [],
            selectedCount: 0,
            defaultExpandedKeys: [],
            allNodeIds: [],
            props: {
                label: "name",
                children: "children"
            },
            treeData: []
        }
    },
    watch: {
        curChoosedData(val){
            this.$emit('curChoosedDataChange', val);
        },
    },
    computed: {
        isSingle() {
            return this.selection === 'single'
        }
    },
    created(){
        const data = [],
        root = 8,
        children = 3,
        base = 9000;
      for (let i = 0; i < root; i++) {
        data.push({
          id: `${i}`,
          name: `test-${i}`,
          children: []
        });
        for (let j = 0; j < children; j++) {
          data[i].children.push({
            id: `${i}-${j}`,
            name: `test-${i}-${j}`,
            children: []
          });
          for (let k = 0; k < base; k++) {
            data[i].children[j].children.push({
              id: `${i}-${j}-${k}`,
              name: `test-${i}-${j}-${k}`
            });
          }
        }
      }
      this.treeData = data;
    },
    mounted() {
        this.getSelectedCount()
    },
    methods: {
        expandedLevel(num = 1){
            const treeCompRef = this.$refs.treeComp;
            this.treeOption.forEach((item)=>{
                item.treeData = this.$lodash.cloneDeep(item.treeData)
            })
            if(treeCompRef && treeCompRef.length > 0) {
                for (const [index,item] of treeCompRef.entries()) {
                    let checkedKeys = item.getCheckedKeys()
                    let treeData = item.data
                    this.defaultExpandedKeys[index] = this.expandedReduce(treeData, num)
                    item.setCheckedKeys(checkedKeys)
                }
            }
        },
        //递归获取展开层级的id
        expandedReduce(list,deep = 1){
           return deep > 0 ? list.reduce((val ,next)=>{
                   return next.children? val.concat(next.id).concat(this.expandedReduce(next.children,deep-1)) : val.concat(next.id)
               },[]) : []
        },
        // 过滤值改变触发filterNode
        onFilter(filterVal) {
            const treeCompRef = this.$refs.treeComp;
            if(treeCompRef && treeCompRef.length >0){
                for (let item of treeCompRef) {
                    this.filterParentNodeId = [];
                    item.filter(filterVal);
                }
            }
        },

        // 筛选树节点
        filterNode(value, data) {
            if (!value) return true;
            let filterValue = value.split(',');
            let flag = false;
            filterValue.forEach((item) => {
                if (data.name.indexOf(item) !== -1 || this.filterParentNodeId.includes(data.parentId)) {
                    this.filterParentNodeId.push(data.id);
                     flag = true;
                } 
            });
            return flag;
        },
        handleCheckChange:function (data, checked) {
            if (this.isSingle) {
                this.singleCheck(data,checked)
            }
            this.getSelectedCount()
        },
        singleCheck:debounce(function (data,checked){
            this.$nextTick(()=>{
                if (checked) {
                    this.$refs.treeComp[0].setCheckedKeys([data.id]);
                }
            })
        },100),
        getSelectedCount: debounce(function () {
            this.selectedCount = 0
            const treeCompRef = this.$refs.treeComp;
            if(treeCompRef && treeCompRef.length >0){
                for (const item of treeCompRef) {
                    let selectedNodes = item.getCheckedNodes()
                    let selectedChildrenNodes = selectedNodes.filter((node) => {
                        // !Object.prototype.hasOwnProperty.call(node, 'children')
                        // return node.children.length === 0
                        return !node.children || node.children.length === 0
                    })
                    this.selectedCount += selectedChildrenNodes.length
                }
            }
            this.$emit('getSelectedCount', this.selectedCount)
        },300)
    }
}
</script>
<style scoped>
    .tree-comp {
        display: flex;
        flex-direction: column;
        overflow: hidden;
        width: 100%;
        height: 100%;
    }

    .tree-comp .input-box >>> .el-input__inner {
        border: none;
        border-radius: 0;
        border-bottom: 1px solid #A8AED3;
        height: 32px;
        line-height: 32px;
    }

    .tree-comp .input-box >>> .el-input__suffix-inner {
        line-height: 32px;
        font-size: 16px;
    }

   .tree-comp .el-tree {
        /* flex: 1; */
        max-height: 100%;
        overflow-y: auto;
    }

    .tree-node {
        flex: 1;
        height: 30px;
        line-height: 30px;
        overflow: hidden;
        white-space: nowrap;
        text-overflow: ellipsis;
        /*background: #fff;*/
        z-index: 2;
    }
    .tree-self {
        width: 100%;
        overflow: scroll;
    }
    .tree-self >>> .el-tree-node {
        min-width: 100%;
        display: table;
    }
</style>

7.效果

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1854015.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

大模型参数高效微调学习笔记

大模型参数高效微调学习笔记 github地址 billbill链接 1.分类 图中有五个大类&#xff1a; selective&#xff08;选择性微调&#xff09;&#xff1a;BitFit&#xff0c;Attention Tuningsoft prompts&#xff08;提示微调&#xff09;&#xff1a;Prompt-tuning&#xff0c…

实战指南:部署Elasticsearch 8.4.1与Kibana 8.4.1并集成IK分词器

首先拉取elasticsearch和kibana镜像 docker pull elasticsearch:8.4.1 docker pull kibana:8.4.1如果遇到镜像拉去不下来&#xff0c;遇到如下问题&#xff1a; [ERROR] error pulling image configuration: Get " https://production.cloudflare.docker.com/registry-v…

[Spring Boot]Netty-UDP客户端

文章目录 简述Netty-UDP集成pom引入ClientHandler调用 消息发送与接收在线UDP服务系统调用 简述 最近在一些场景中需要使用UDP客户端进行&#xff0c;所以开始集成新的东西。本文集成了一个基于netty的SpringBoot的简单的应用场景。 Netty-UDP集成 pom引入 <!-- netty --…

Adaboost集成学习 | Adaboost集成学习特征重要性分析(Python)

目录 效果一览基本介绍模型设计程序设计参考资料效果一览 基本介绍 Adaboost集成学习特征重要性分析(Python)Adaboost(自适应增强)是一种常用的集成学习方法,用于提高机器学习算法的准确性。它通过组合多个弱分类器来构建一个强分类器。在Adaboost中,每个弱分类器都被赋予…

电子电气架构——由NRC优先级引起的反思

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明自己,无利益不试图说服别人,是精神上的节…

【论文精读】分类扩散模型:重振密度比估计(Revitalizing Density Ratio Estimation)

文章目录 一、文章概览&#xff08;一&#xff09;问题的提出&#xff08;二&#xff09;文章工作 二、理论背景&#xff08;一&#xff09;密度比估计DRE&#xff08;二&#xff09;去噪扩散模型 三、方法&#xff08;一&#xff09;推导分类和去噪之间的关系&#xff08;二&a…

shell的正则表达式

一、正则表达式&#xff1a;匹配的是文本内容&#xff08;文本三剑客&#xff1a;grep&#xff1a;过滤文本内容 sed&#xff1a;针对文本内容进行增删改查 awk&#xff1a;按行取列&#xff09; 二、grep&#xff1a;过滤 1.grep&#xff1a;过滤文本内容&…

基于协方差信息的Massive MIMO信道估计算法性能研究

1. 引言 随着移动互联网不断发展&#xff0c;人们对通信的速率和可靠性的要求越来越高[1]。目前第四代移动通信系统已经逐渐商用&#xff0c;研究人员开始着手研究下一代移动通信系统相关技术[2][3]。在下一代移动通信系统中要求下行速率达到10Gbps&#xff0c;这就要求我们使…

秋招突击——第八弹——Redis是怎么运作的

文章目录 引言正文Redis在内存中是怎么存储的面试重点 Redis是单线程还是多线程面试重点 内存满了怎么办&#xff1f;面试重点 持久化介绍面试重点 RDB持久化面试重点 AOF日志面试重点 总结 引言 差不多花了两天把redis给过了&#xff0c;早上也只背了一半&#xff0c;完成回去…

【database1】mysql:DDL/DML/DQL,外键约束/多表/子查询,事务/连接池

文章目录 1.mysql安装&#xff1a;存储&#xff1a;集合&#xff08;内存&#xff1a;临时&#xff09;&#xff0c;IO流&#xff08;硬盘&#xff1a;持久化&#xff09;1.1 服务端&#xff1a;双击mysql-installer-community-5.6.22.0.msi1.2 客户端&#xff1a;命令行输入my…

<router-view />标签的理解

< router-view />标签的理解 < router-view />用来承载当前级别下的子集路由的一个视图标签。显示当前路由级别下一级的页面。 App.vue是根组件&#xff0c;在它的标签里使用&#xff0c;而且配置好路由的情况下&#xff0c;就能在浏览器上显示子组件的效果。 如…

模拟算法讲解

模拟算法是一种基于实际情况模拟的算法&#xff0c;通过模拟现实世界中的系统或过程&#xff0c;来研究它们的性质和行为。模拟算法可以用于解决各种问题&#xff0c;包括物理模拟、经济模拟、社会模拟等。 模拟算法的基本步骤包括&#xff1a; 定义问题&#xff1a;明确需要模…

用Java获取键盘输入数的个十百位数

这段Java代码是一个简单的程序&#xff0c;用于接收用户输入的一个三位数&#xff0c;并将其分解为个位、十位和百位数字&#xff0c;然后分别打印出来。下面是代码的详细解释&#xff1a; 导入所需类库: import java.util.Scanner;&#xff1a;导入Scanner类&#xff0c;用于从…

已解决java.util.concurrent.BrokenBarrierException异常的正确解决方法,亲测有效!!!

已解决java.util.concurrent.BrokenBarrierException异常的正确解决方法&#xff0c;亲测有效&#xff01;&#xff01;&#xff01; 目录 问题分析 出现问题的场景 报错原因 解决思路 解决方法 分析错误日志 检查线程中断 设置合理的等待时间 优化代码逻辑 使用同步…

登录安全分析报告:链家地产

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞 …

【SpringCloud】负载均衡(Spring Cloud LoadBalancer)

负载均衡 当服务流量增大时&#xff0c;通常会采用增加机器的方式进行扩容。负载均衡就是用来在多个机器或者其他资源中&#xff0c;按照一定的规则合理分配负载。其中的实现可以分成客户端负载均衡和服务端负载均衡。 服务端负载均衡 在服务端进行负载均衡的算法分配。 比…

【CSS in Depth2精译】1.2 继承~1.3 特殊值

文章目录 1.2 继承1.3 特殊值1.3.1 inherit 关键字1.3.2 initial 关键字1.3.3 unset 关键字1.3.4 revert 关键字 1.2 继承 除了层叠&#xff0c;还有一种给元素设置样式的方式&#xff1a;继承。经常有人把层叠与继承的概念弄混淆。它们虽然有关联&#xff0c;但也应该分辨清楚…

mysql中in参数过多该如何优化

优化方式概述 未优化前 SELECT * FROM rb_product rb where sku in(1022044,1009786)方案2示例 public static void main(String[] args) {//往list里面设置3000个值List<String> list new ArrayList<>();for (int i 0; i < 3000; i) {list.add(""…

聚焦AIoT最后一公里:EasyCVR+AI视频技术在各领域的创新应用

随着5G、AI、边缘计算、物联网&#xff08;IoT&#xff09;、云计算等技术的快速发展&#xff0c;万物互联已经从概念逐渐转变为现实&#xff0c;全新的行业生态AIoT正在开启新时代。巨大的市场潜力与AI等新兴技术不断融合形成的庞大市场缺口&#xff0c;深度场景化应用落地诉求…

express+vue 在线五子棋(一)

示例 在线体验地址五子棋&#xff0c;记得一定要再拉个人才能对战 本期难点 1、完成了五子棋的布局&#xff0c;判断游戏结束 2、基本的在线对战 3、游戏配套im(这个im的实现&#xff0c;请移步在线im) 下期安排 1、每步的倒计时设置 2、黑白棋分配由玩家自定义 3、新增旁观…