tree 的节点懒加载

1,169 阅读2分钟
  1. 在实际项目开发中,当 tree 的某个节点大于 2000 个时,dom 渲染起来就非常慢,整个页面就会卡了起来.
  2. 基于 vue elementUI 来开发,用到了 InfiniteScroll 无限滚动 Tree 树形控件
  3. 来个 demo,具体看下 tree 子节点过多时的卡顿吧。
  1. 优化思路就是 前端刚开始只加载 10 个子节点,当滚动到底部时,再增加 10 个。
<div id="app">
  <div
    class="tree-box1"
    :infinite-scroll-immediate="false"
    v-infinite-scroll="load"
  >
    <el-tree
      :data="curList"
      v-loading="loading"
      default-expand-all
      :props="defaultProps"
      @node-click="handleNodeClick"
    ></el-tree>
  </div>
</div>
<style>
  .tree-box1 {
    height: 200px;
    overflow: auto;
  }
</style>

<script>
  var Main = {
    data() {
      return {
        loading: false,
        curtreePageSize: 10,
        allList: [],
        curList: [],
        defaultProps: {
          children: 'children',
          label: 'label',
        },
      }
    },
    methods: {
      //滚动轴滚动最底部触发,tree的数组动态增加
      load() {
        if (this.allList.length) {
          console.log('滚动')
          this.curtreePageSize += 10
          this.curList = this.sliceTree(this.allList, this.curtreePageSize)
        }
      },
      handleNodeClick(data) {
        console.log(data)
      },
      //tree数组的截取,默认截取10个,可以动态配置
      sliceTree(data, len = 10) {
        return [
          {
            label: '一级 1',
            children: data.slice(0, len),
          },
        ]
      },
      // 这是模拟跟后端的请求;用setTimeout模拟
      impersonationRequest() {
        this.loading = true
        let arr = []
        for (let i = 1; i < 4000; i++) {
          arr.push({ label: `二级 1-${i}` })
        }
        setTimeout(() => {
          this.allList = arr
          this.curList = this.sliceTree(this.allList)
          this.loading = false
        }, 1000)
      },
    },
    mounted() {
      this.impersonationRequest()
    },
  }
  var Ctor = Vue.extend(Main)
  new Ctor().$mount('#app')
</script>
  1. 好了基本完结,假如你不用el-tree自带的关键字过滤树节点的话。 又或者后端大佬愿意帮你写个接口,返回过滤后的tree给你; 不然的话,你就自己来实现了。el-tree本身的过滤是没有问题的,但是你用了懒加载,例如只加载了10个,它只会在你10个里面来进行搜索;你就需要自己写个算法来实现; 核心代码如下
/**
 * data 为 tree数据
 * name 为 筛选关键词
 **/
const filterTree  = (data, name) => {
           // 当name数据为空时,返回默认的10个数据dom
           if (!name) {
               this.curList = this.sliceTree(this.allList)
               return
           }
           const PrimaryNode = [...data] //数组
           // 递归的方法,来判断
           function traverse (node) {
               node.forEach(v => {
                   //假如当前节点的属性满足 关键词,则将当前节点的visible属性设置为tree
                   v.visible = v.label.includes(name)
                   //如果纯在子节点,就递归调用本函数
                   if (v.children) {
                       traverse(v.children)
                   }
                   /* 当前节点visible为false, 且有子节点的再判断一次
                    假如子节点存在visible为true,则父节点的visible也设置成tree
                    这个涉及到递归的执行顺序问题,我们假如搜索的孙子节点三级,
                    const tree = [{
                       label:'一级',
                       children:[{
                           label: '二级',
                           children:[{
                           label: '三级',
                           }]
                       }]
                   }]
                   下面代码执行顺序是从最深处开始执行,然后渐渐到外层的,所以不会有问题;
                   */
                   if (!v.visible && v.children) {
                       v.visible = v.children.some(child => child.visible)
                   }
               })
           }
           //第二个函数的作用,就是过滤掉visible为false的选项。比较容易理解
           function filterName (node) {
               const newNode = node.filter(v => v.visible)
               newNode.forEach(v => {
                   if (v.children) {
                       v.children = filterName(v.children)
                   }
               })
               return newNode
           }
           traverse(PrimaryNode)
           const filterNode = filterName(PrimaryNode).slice(0,100)
           if(filterNode.length) {
              this.curList = [{
                 label: '一级 1',
                 children: filterNode
                 }] 
           }else {
             this.curList = []
           }
       },