tree 树组件大数据卡顿问题处理

问题背景

项目中有用到树组件用来做文件目录,但是由于这个树组件的节点越来越多,导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多,导致的浏览器卡顿,这里很明显就需要用到虚拟列表的技术(即不渲染这么多的节点)。

解决方案

如果是vue3,那么直接使用element-plus提供的Tree V2 虚拟化树形控件组件即可。但是因为这个项目是一个vue2的项目,并且它原先用的是iview的组件来实现的,我这边不可能因为这个优化就把项目升级到vue3,所以有两个解决思路: 1.从网上找轮子 2.自己造轮子(时间太紧,放弃) 。找了很久终于找到一个比较好用的轮子,分享给大家使用,以后有遇到相同问题的时候可以参考下。

解决过程:

使用vue-easy-tree来优化树组件(文档地址

1.安装虚拟树组件,以及安装sass和sass-loader

vue 复制代码
npm i @fortawesome/fontawesome-free@6.7.2 -s

由于这个项目依赖了saas,所以要把这个也安装下

vue 复制代码
npm i sass@1.89.1 sass-loader@7.3.1 -D

2.导入使用

vue 复制代码
import VueEasyTree from '@wchbrad/vue-easy-tree'
import '@wchbrad/vue-easy-tree/src/assets/index.scss'

   components: {
    VueEasyTree
  },

3.替换el-tree来使用

注意:该组件和element-ui的tree组件的prop是一致的(所以使用的使用可以参考element的tree组件),但是加了height属性时就会开启虚拟列表

4.完整代码

vue 复制代码
<template>
  <div class="home">
    <div class="left_box">
      <!-- <el-tree :data="treeData" :props="props" ref="veTree" node-key="id" :default-expanded-keys="['Root']"
        class="op-tree" :render-content="treeRender"></el-tree> -->
        <vue-easy-tree
          ref="veTree"
          node-key="id"
          class="op-tree"
          height="calc(100vh - 110px)"
          :default-expanded-keys="['Root']"
          :data="treeData"
          :props="props"
          :render-content="treeRender"
        ></vue-easy-tree>
    </div>

  </div>
</template>

<script>
import treeData from './treeData'
import VueEasyTree from '@wchbrad/vue-easy-tree'
import '@wchbrad/vue-easy-tree/src/assets/index.scss'

export default {
  name: 'HomeView',
  components: {
    VueEasyTree
  },
  computed: {
  },
  data() {
    return {
      treeData: treeData,
      props: {
        children: 'children',
        label: 'title'
      },
      localFile: {
        title: '',
        nodeId: '',
        nodeType: '',
        pId: ''
      },
    }
  },
  created() {
    console.log('treeData', this.treeData)
  },
  methods: {
    treeRender(h, { data }) {
      // nodeType 'file','文件','dir','文件夹'
      return (
        <div class={this.localFile.nodeId === `${data.id === 'Root' ? '/' : data.id.replace('Root', '')}` ? 'active tree-item' : 'tree-item'}>
          <span on-click={() => { this.changeCode(data, `${data.nodeType}Detail`) }} class={this.localFile.nodeId === `${data.id === 'Root' ? '/' : data.id.replace('Root', '')}` ? 'colorBlue tree-item-title textOverflow' : 'tree-item-title textOverflow'}>
            {
              data.nodeType === 'file' ? <i class="fa fa-file"></i> : data.nodeType === 'dir' ? <i class="fa fa-folder-open"></i> : <span></span>
            }
            &ensp;
            {data.title}
          </span>
          <div class="button-group">
            {
              data.nodeType !== 'file'
                ? <tooltip content="新增" transfer placement="top">
                  <i class="fa fa-plus" on-click={() => { this.changeCode(data, 'add') }}></i>
                </tooltip> : <span></span>
            }
            {
              data.id !== 'Root'
                ? <tooltip content="修改" transfer placement="top">
                  <i class="fa fa-edit" on-click={() => { this.changeCode(data, 'modify') }}></i>
                </tooltip> : <span></span>
            }
            {
              data.id !== 'Root'
                ? <tooltip content="删除" transfer placement="top">
                  <i class="fa fa-trash" on-click={() => { this.changeCode(data, 'delete') }}></i>
                </tooltip> : <span></span>
            }
          </div>
        </div>
      )
    },
    changeCode(data = '', type) {
      this.view = ''
      this.$nextTick(() => {
        if (data.title && data.id) {
          this.localFile = {
            title: data.title,
            nodeId: `${data.id === 'Root' ? '/' : data.id.replace('Root', '')}`,
            nodeType: data.nodeType,
            pId: data.pId
          }
          type === `${data.nodeType}Detail` ? this.handleDetail(data.nodeType)
            : type === 'add' ? this.handleAdd()
              : type === 'modify' ? this.handleEdit()
                : type === 'delete' ? this.handleDelete() : ''
        } else {
          this.$Notice.warning({
            title: '提醒',
            desc: '找不到当前信息',
            duration: 3
          })
        }
      })
    },
    handleDetail(){
      console.log('详情')
    },
    handleEdit() {
      console.log('编辑')
    },
    handleAdd() {
      console.log('新增')
    },
    handleDelete() {
      console.log('删除')
    },
  },

};
</script>
<style lang="less" scoped>
.home {
  .left_box {
    width: 300px;
    height: 90vh;
    border: 1px solid #ccc;
    overflow-y: auto;
  }
}
</style>

总结

很多项目由于前期数据量不大,所以组件可以正常使用,但是当用户量或者数据量大了之后,浏览器就会变得卡顿,这种优化就是必须的。大家可以把代码拉下来运行对比下,没有使用虚拟列表技术的树真的很卡。

项目地址

gitHub:github.com/rui-rui-an/...

相关推荐
mldong7 小时前
你的 Vue3 项目也能有钉钉同款审批流设计器:npm 装包,10 分钟画出第一条审批流
前端·vue.js
2分钟速写快排8 小时前
什么是 RAG?如何用 RAG 实现一个用户记忆?
前端·后端·ai编程
passerby60618 小时前
如何自己造一个时间处理库
前端·javascript·github
走到天涯海角9 小时前
react里面的长列表渲染优化
前端·react.js·前端框架
小羊没烦恼!9 小时前
Hello Web API系列教程——Web API与国际化
java·服务器·前端·javascript·php
北岛贰10 小时前
迷茫焦虑期,我做了一个带支付带官网的 AI 聊天虚拟恋人 App
前端·人工智能·后端
mayaairi11 小时前
Vue2 组件通讯(三):全局事件总线、PubSub、插槽与组件实例属性
前端·javascript·vue.js
kyriewen12 小时前
面试官问我:AI 都能写代码了,前端凭什么还值 25K
前端·javascript·人工智能
风骏时光牛马12 小时前
AI源码分析:拆解模型底层实现逻辑
前端
IT_陈寒13 小时前
React子组件莫名其妙重渲染?你可能漏了这个Hook
前端·人工智能·后端