1. 出现的问题

要展示树形数据表格,根据当前点击的表格行去请求新的数据并展示, 基于这种情况遇到以下问题
1). 当树形表格数据层级大于五级且数据量较多时, 浏览器崩溃
2). 当数据条数展示超出500以上时页面明显卡顿, 加载越多越卡直到崩溃
3). 2020-09-18 添加: 火狐浏览器, 底部树形展开然后切换表格展示数据时虚拟表格渲染出现空白如下图
表格出现空白

2. 根据问题找出解决方案

开发使用vue + element-ui 框架 基于el-table 尝试解决方案
猜测是表格数据切换但是虚拟表格定位展示出现问题, 因为重新滚动即可完整展示
解决方案: 重新绘制表格

2.1 不使用树形表格数据, 对数据做平铺展示, 手动模拟树形数据的功能及相关数据处理
// 模拟的操作的元素结构
<template slot-scope="scope">
   <div>
	   <i 
		   v-if="scope.row.hasChildren" 
		   :class="getClassName(scope.row)" 
		   class="level-icon" 
		   @click.stop="load(scope.row, scope.$index)"
		   >
	   </i>
	   <span 
	   	:class="`span-level${scope.row.level}`">
		   {{scope.row.dimenssionName}}
	   </span>
   </div>
</template>

// methods 中的方法 
getTableData () {
	// 对获取的数据做处理
	/*
		1. 对数据做标识处理
		添加level 代表当前数据所处层级关系
		添加expend 代表当前树形结构展示收起状态及icon 展示
		添加loading 代表当前树形结构点击后接口请求加载状态 icon的展示
		添加nodeString 字段标识取上一级的 nodeString字段和当前数据的 id 拼接用于标识展开数据的父子层级关系 
	*/
}
getClassName (row) { // 根据参数设置 icon 以及当前层级
	 return {
	    'el-icon-loading': row.loading,
	    'el-icon-arrow-right': !row.expend && !row.loading,
	    'el-icon-arrow-down': row.expend && !row.loading,
	    [`level${row.level}`]: row.level
	  }
},

load(row, $index) { // 获取数据的方法
	/*
	此处根据当前点击的这一行 row 判断这一行是处于展开还是收起状态,
	如果是展开状态则收起并清除当前节点下的所有子节点
	如果是展开状态则请求新数据并添加到当前位置的后续位置
	同时需要修改按钮的状态
	*/ 
	// 修改按钮的loading 状态
	this.$set(this.tableData[$index], 'loading', true)
	// 获取当前的点击行的展开收起状态
	const expend = this.tableData[$index].expend
	if (expend) { // 处于展开需要收起
		// 因为都是单层数据, 没有循环引用使用 JSON.parse, JSON.stringify 克隆数据
		const data = JSON.parse(JSON.stringify(this.tableData))
		const tableData = data.filter(item => { // 对当前数据进行过滤
			// 通过 nodeString 字段判断 去除的数据一定是当前点击行的子数据
			if (item.nodeString.includes(row.nodeString) && item.level > row.level) {
	            item.expend = false
	            return false
          	} else {
	            return true
          	}
		})
		this.tableData = tableData // 数据重新赋值
		// 修改按钮状态
		this.$set(this.tableData[$index], 'expend', !expend)
		this.$set(this.tableData[$index], 'loading', false)
		return  // 不继续向下执行
	}
	
	// 处于收起状态则进行展开
	try {
		// 请求数据 假设获取的数据为 data 是个数组
		if (!data.length) { // 如果数组没有长度 则去除当前行树形展开按钮
            this.$set(this.tableData[$index], 'hasChildren', false)
        }
		// 对当前点击位置数据进行截取
		const preData = this.tableData.slice(0, $index + 1) // 从开始到点击位置的数据
        const nextData = this.tableData.slice($index + 1) // 从点击位置后一个到结束位置的数据
        data.map(item => { // 添加关键字段意义同getTableData 方法中说明
           item.parentNodeId = row.id
           item.level = row.level + 1
           item.loading = false
           item.nodeString = `${row.nodeString}_${item.id}`
       	})
       	// 拼接数据
       	const tableData = preData.concat(data).concat(nextData)
       	const filterData = tableData.filter(item => {
              let flag = true
              const rowRoot = row.nodeString.split('_')[0]
              if (!item.nodeString.includes(rowRoot) && row.level === 5) { // 剔除根为5级展开的数据, 只保留当前
                if (item.level === 6) {
                  item.expend = false
                  flag = false
                } else if (item.level === 5) {
                  item.expend = false
                }
              }
              return flag
            })
       	// 重新赋值
       	this.tableData = filterData
	} catch (e) { // 关闭icon loading状态
		this.$set(this.tableData[$index], 'loading', false)
	}
}
2.2 以上的数据处理及问题说明

出现的问题:

  1. 多个五级数据展开时, 其他自动收起, 滚动条定位错误,表格出现跳动,展示不准确
  2. 多数据树形展开是表格卡顿

解决办法:

  1. 通过多方查找验证决定使用 umy-ui 大数据量虚拟表格实现

虚拟表格是只渲染少量表格元素, 随着鼠标滚动, 动态的切换数据进行表格数据渲染, 这就解决了多数据量表格卡顿问题

3. 使用umy-ui: u-table 组件

<template>
  <div class="table-box" ref="tableBox">
    <u-table
      :data="tableData"
      style="width: 100%;"
      ref="uTable"
      :max-height="height"
      :data-changes-scroll-top="false"
      :empty-text="emptyText"
      use-virtual
      border
    >
      <!--u-table大数据表格 你需要在列上指定某个列显示展开收起 treeNode属性, 这里我是用自己模拟的所以没有指定treeNode属性-->
          <u-table-column
             :tree-node="true"
             prop="address"
             label="我是树节点"
             fixed
             :width="200"/>
          <u-table-column
             v-for="item in columns"
             :key="item.id"
             :prop="item.prop"
             :label="item.label"
             :width="item.width"/>
    </u-table>
  </div>
// 使用u-table 后对上面数据做了重新处理
load (row, $index) {
	/*
		此处根据当前点击的这一行 row 判断这一行是处于展开还是收起状态,
		如果是展开状态则收起并清除当前节点下的所有子节点
		如果是展开状态则请求新数据并添加到当前位置的后续位置
		同时需要修改按钮的状态
	*/ 
      this.$set(this.tableData[$index], 'loading', true) // 开启loading状态
      const expend = this.tableData[$index].expend // 获取展开状态
      if (expend) {
        const data = JSON.parse(JSON.stringify(this.tableData))
        const tableData = []
        data.map(item => {
          if (item.nodeString.includes(row.nodeString) && item.level > row.level) {
            item.expend = false
          } else {
            tableData.push(item)
          }
        })
        this.tableData = tableData
        this.$set(this.tableData[$index], 'expend', !expend)
        this.$set(this.tableData[$index], 'loading', false)
        return
      }
      try {
        this.axios({ // 请求数据
          url: '/api/tool/info',
          method: 'POST',
          data: params
        }).then(res => {
          if (成功了) {
            const data = JSON.parse(JSON.stringify(res.data.data))
            if (!data.length) { // 异常处理
              this.$set(this.tableData[$index], 'hasChildren', false) // 去除按钮可展开状态
            }
            this.$set(this.tableData[$index], 'expend', !expend)
            this.$set(this.tableData[$index], 'loading', false)
            const preData = this.tableData.slice(0, $index + 1) // 获取点击位置及之前前数据
            const nextData = this.tableData.slice($index + 1) // 点击位置后数据
            const length = preData.length + data.length - 1
            data.map(item => { // 对新获取数据添加标识及层级关系
              item.parentNodeId = row.id
              item.level = row.level + 1
              item.loading = false
              item.length = length
              item.nodeString = `${row.nodeString}_${item.id}`
            })
            const tableData = preData.concat(data).concat(nextData)
  			 因使用虚拟表格没有性能问题, 所以去除收起功能
            // const filterData = tableData.filter(item => { 
            //   let flag = true
            //   const rowRoot = row.nodeString.split('_')[0]
            //   if (!item.nodeString.includes(rowRoot) && row.level === 5) { // 剔除根为5级展开的数据, 只保留当前
            //     if (item.level === 6) {
            //       item.expend = false
            //       flag = false
            //     } else if (item.level === 5) {
            //       item.expend = false
            //     }
            //   }
            //   return flag
            // })
            this.tableData = tableData
          } else {
            this.$set(this.tableData[$index], 'loading', false)
          }
        }, err => {
          console.log(err)
          this.$set(this.tableData[$index], 'loading', false)
        })
      } catch (e) {
        this.$set(this.tableData[$index], 'loading', false)
      }
    }

4. 总结

经过以上处理解决了表格性能问题
umy-ui地址

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐