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

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.效果

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ARM32开发--WDGT看门狗

知不足而奋进 望远山而前行 目录 文章目录 前言 目标 内容 什么是看门狗 ARM中的看门狗 独立看门狗定时器 窗口看门狗定时器 独立看门狗FWDGT 初始化配置 喂狗 完整代码 窗口看门狗WWDGT 初始化配置 喂狗 完整代码 注意 总结 前言 嵌入式系统在如今的科技发…

OpenGL3.3_C++_Windows(18)

接口块&#xff1a; glsl彼此传输数据&#xff0c;通过in / out&#xff0c;当更多的变量&#xff0c;涉及数组和结构体接口块(Interface Block)类似struct&#xff0c;in / out 块名{……}实例名 Uniform缓冲对象&#xff1a; 首先理解uniform Object&#xff1a;负责向gl…

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

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

Debian Linux安装minikubekubectl

minikube&kubectl minkube用于在本地开发环境中快速搭建一个单节点的Kubernetes集群,还有k3s&#xff0c;k3d&#xff0c;kind都是轻量级的k8skubectl是使用K8s API 与K8s集群的控制面进行通信的命令行工具 这里使用Debian Linux演示&#xff0c;其他系统安装见官网,首先…

完美解决找不到steam_api64.dll无法执行代码问题

游戏缺失steam_api64.dll通常意味着该游戏依赖于Steam平台的一些功能或服务&#xff0c;而这个DLL文件是Steam客户端的一部分&#xff0c;用于游戏与Steam平台之间的交互。如果游戏中缺失这个文件&#xff0c;可能会出现无法启动、崩溃或其他问题。 一&#xff0c;详细了解stea…

Java内存泄漏检测和分析介绍

在Java中&#xff0c;内存泄漏检测和分析是一个重要的任务&#xff0c;可以通过以下几种方式进行&#xff1a; 1. 使用VisualVM VisualVM是一个可视化工具&#xff0c;可以监控、分析Java应用程序的内存消耗。它可以显示堆内存、垃圾收集、线程等信息&#xff0c;并且可以对内…

Linux - 利用/proc/sys/vm/drop_caches实现手工清理系统缓存

文章目录 现象buff/cache 的作用和含义分析 buff/cache 占用大量内存的原因是否需要清理缓存及其方法 命令清理缓存方法1. sync 命令2. echo 3>/proc/sys/vm/drop_caches 命令 注意事项小结 现象 使用free 命令&#xff0c;看到 buff/cache 占用很多 。 free 命令用于显示系…

用 idea 启动多个实例

在学习负载均衡的时候&#xff0c;要模拟多个实例均提供一个服务&#xff0c;我们要如何用 idea 启动多个实例呢&#xff1f; 如下图&#xff0c;我们已经启动了一个 ProductService 服务&#xff0c;现在想再启动两个相同的服务 1. 选中要启动的服务,右键选择 Copy Configura…

【机器学习】音乐大模型的深入探讨——当机器有了创意,是机遇还是灾难?

&#x1f440;国内外音乐大模型基本情况&#x1f440; ♥概述♥ ✈✈✈如FreeCompose、一术科技等&#xff0c;这些企业专注于开发人工智能驱动的语音、音效和音乐生成工具&#xff0c;致力于利用核心技术驱动文化产业升级。虽然具体公司未明确提及&#xff0c;但可以预见的是…

docker搭建mongo副本集

1、mongo集群分类 MongoDB集群有4种类型&#xff0c;分别是主从复制、副本集、分片集群和混合集群。 MongoDB的主从复制是指在一个MongoDB集群中&#xff0c;一个节点&#xff08;主节点&#xff09;将数据写入并同步到其他节点&#xff08;从节点&#xff09;。主从复制提供…

图像数字化基础

一、像素 1、获取图像指定位置的像素 import cv2 image cv2.imread("E:\\images\\2.png") px image[291,218] print("坐标(291,218)上的像素的BGR值是&#xff1a;",px) &#xff08;1&#xff09;RGB色彩空间 R通道&#xff1a;红色通道 G通道&…

Failed to establish a new connection: [WinError 10061] 由于目标计算机积极拒绝,无法连接

在进行参数化读取时发现一个问题&#xff1a; 发现问题&#xff1a; requests.exceptions.ConnectionError: HTTPConnectionPool(hostlocalhost, port8081): Max retries exceeded with url: /jwshoplogin/user/update_information.do (Caused by NewConnectionError(<url…

MFC学习--CListCtrl复选框以及选择

如何展示复选框 //LVS_EX_CHECKBOXES每一行的最前面带个复选框//LVS_EX_FULLROWSELECT整行选中//LVS_EX_GRIDLINES网格线//LVS_EX_HEADERDRAGDROP列表头可以拖动m_listctl.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_CHECKBOXES | LVS_EX_GRIDLINES); 全选&#xff0c;全…

React+TS 从零开始教程(2):简中简 HelloWolrd

源码链接&#xff1a;https://pan.quark.cn/s/c6fbc31dcb02 这一节&#xff0c;我们来见识ReactTS的威力&#xff0c;开始上手开发第一个组件&#xff0c;什么组件呢&#xff1f; 当然是简中简的 HelloWolrd组件啦。 在src下创建一个components&#xff0c;然后新建Hello.tsx …

车载测试系列:CAN协议之远程帧

远程帧&#xff08;也叫遥控帧&#xff09;&#xff1a;是接收单元向发送单元请求发送具有标识符的数据所用的帧&#xff0c;由 6 个段组成&#xff0c;没有数据段。 当某个节点需要数据时&#xff0c;可以发送远程帧请求另一节点发送相应数据帧。 简单的说&#xff1a;发起方…

数据库理论大题与编译原理大题(笔记)

目录 数据库&#xff08;求最小函数依赖&#xff09; 数据库&#xff08;求属性集的闭包和候选码&#xff09; 编译原理&#xff08;NFA ——> DFA&#xff09; 编译原理&#xff08;识别文法的活前缀 DFA 和 LR(0) 分析表&#xff09; 哈哈&#xff01;这是本人作者才…

计算机组成入门知识

前言&#x1f440;~ 数据库的知识点先暂且分享到这&#xff0c;接下来开始接触计算机组成以及计算机网络相关的知识点&#xff0c;这一章先介绍一些基础的计算机组成知识 一台计算机如何组成的&#xff1f; 存储器 CPU cpu的工作流程 主频 如何衡量CPU好坏呢&#xff1f…

多路h265监控录放开发-(12)完成全部开始录制和全部停止录制代码

xviewer.h 新增 public: void StartRecord();//126 开始全部摄像头录制 void StopRecord();//126 停止全部摄像头录制 xviewer.cpp 新增 //视频录制 static vector<XCameraRecord*> records;//126void XViewer::StartRecord() //开始全部摄像头录制 126 {StopRecord…

leetcode刷题日记

题目描述 解题思路 基本思想&#xff0c;将数组复制一份&#xff0c;按照位置取余&#xff0c;确实做出来了&#xff0c;但是这样时间和空间上的资源比较多。看到切片法&#xff0c;感觉到很新&#xff0c;思路很好&#xff0c;用来记录。 代码 python class Solution:def ro…

C++设计模式——Flyweight享元模式

一&#xff0c;享元模式简介 享元模式是一种结构型设计模式&#xff0c;它将每个对象中各自保存一份数据的方式改为多个对象共享同一份数据&#xff0c;该模式可以有效减少应用程序的内存占用。 享元模式的核心思想是共享和复用&#xff0c;通过设置共享资源来避免创建过多的实…