大数据渲染集成方案

145 阅读3分钟

一、背景

当前系统平台的不少页面比如(分布式机器学习相关的页面)需要渲染大量的数据,但是由于数据存储hdfs中,不能用分页的形式去展示只能接口一次性返回所有的数据,在这个情况下如果使用普通的渲染方式,把接口返回的数据一次性全部渲染到当前这个页面上,会导致页面出现渲染过慢,卡顿,滚动不流畅严重影响用户体验,更严重着可能导致页面直接崩溃,用户体验极差,这在产品当中肯定是不允许的,所以这个时候解决大数据渲染的问题就是必要的而且是迫切的。

二、分析

一个页面所能渲染地dom数是有一定限制的,而且一个页面渲染的dom越多,cpu内存开销就越大,一帧所消耗的时间就越长,整个页面看起来就越卡顿,所以要想页面交互流畅体验性好,页面当中就不能过多的去渲染很多个dom ,但是当前的业务需求就是需要一次性展示大量的数据,所以只能从尽量少的渲染dom,然后尽量多的去展示数据的思路去考虑解决这个性能瓶颈的问题,这里少渲染dom多展示数据就意味着只能限制渲染的数量,在这个渲染数量dom的基础上通过滚动或者是下拉来展示不同的数据,渲染的dom数量不变通过来回切不同的数据,达到大数据展示同时又不需要去过多的渲染dom,这在技术手段上叫虚拟渲染的方式,即只对可见区域进行渲染,对非可见区域中的数据不渲染或部分渲染的技术,从而达到极高的渲染性能。

三、设计方案

1.虚拟列表的实现,实际上就是在首屏加载的时候,只加载可视区域内需要的列表项,当滚动发生时,动态通过计算获得可视区域内的列表项,并将非可视区域内存在的列表项删除。

  • 计算当前可视区域起始数据索引(startIndex)
  • 计算当前可视区域结束数据索引(endIndex)
  • 计算当前可视区域的数据,并渲染到页面中
  • 计算startIndex对应的数据在整个列表中的偏移位置startOffset并设置到列表上

由于只是对可视区域内的列表项进行渲染,所以为了保持列表容器的高度并可正常的触发滚动,将Html结构设计成如下结构:

<div class="infinite-list-container">       
   <div class="infinite-list-phantom"></div>      
   <div class="infinite-list">            
   <!-- item-1 --> <!-- item-2 --> <!-- ...... --> <!-- item-n → </div>
</div>
  • infinite-list-container可视区域的容器
  • infinite-list-phantom 为容器内的占位,宽度为总列表的总宽度,用于形成滚动条
  • infinite-list 为列表项的渲染区域

则可推算出:

  • 列表总宽度listWidth = listData.length * itemSize
  • 可显示的列表项数visibleCount = Math.ceil(screenHeight / itemSize)
  • 数据的起始索引startIndex = Math.floor(scrollTop / itemSize)
  • 数据的结束索引endIndex = startIndex + visibleCount
  • 列表显示数据为visibleData = listData.slice(startIndex,endIndex)

当滚动后,由于渲染区域相对于可视区域已经发生了偏移,此时我需要获取一个偏移量startOffset,通过样式控制将渲染区域偏移至可视区域中。

  • 偏移量startOffset = scrollLeft - (scrollLeft % itemSize);

当前是横向滚动大数据渲染的实现原理和思路,纵向滚动大数据虚拟滚动的实现原理和思路是类似的,本文就不在详细往下展开

四、效果

a.横向滚动

b.纵向滚动效果

五、集成步骤

1.下载 npm i vxe-table@3.5.8 -S npm install vite-plugin-style-import -D

**2.**引入到入口文件

import { Column, Table, Grid } from "vxe-table";import "vxe-table/lib/style.css";Vue.use(Column).use(Table).use(Grid);

3.babelrc得配置

4.代码参考

a.横向滚动

<div class="listDetail-container">            <virtual-table              v-else              :data="data"              key="dataPreview"              :columns="columns"              :heightMax="heightMax"              :remain="remain"              :loading="loading"              :itemWidth="itemWidth"            ></virtual-table>  </div>

b.纵向滚动

<template>  <div>        <VxeTableCus          v-if="modelShow"          border          show-overflow          show-header-overflow          ref="xTable"          :data="data"          row-id="name"          :height="maxHeight"          :scroll-y="{ mode: 'default ' }"          :sort-config="{ trigger: 'cell' }"          @radio-change="radioChangeEvent"          @checkbox-change="selectChangeEvent"          :checkbox-config="checkboxConfig"          :radio-config="checkboxConfig"        >          <vxe-column :type="changeType" width="60"></vxe-column>          <vxe-column field="name" title="数据名称" width="200"></vxe-column>          <vxe-column field="type" title="类型" width="200"></vxe-column>        </VxeTableCus>      </div></template><script>  import mlStore from "../mixins/mlStore";  import { hiveOrderSchemas } from "@/api/distMachine";  export default {    data() {      return {        tempSelectItem: [],        maxHeight: 480,        changeType: this.itemType == "col" ? "radio" : "checkbox",        checkboxConfig: {          checkMethod: this.checCheckboxkMethod2.bind(this),          highlight: true,        },      };    },    methods: {      getSelectedCell({ row, column }) {},      //请求hiveSchemas      async requestHiveSchemas(nodes, pNode) {        const idNode = await this.getSuperNode(nodes, pNode);        try {          const res = await hiveOrderSchemas({            id: idNode && idNode.attrs.parameters.dataId.value,            clusterId: this.clusterId,          });          let hiveDatas = [];          for (let v of res) {            hiveDatas.push({              name: v.split(",")?.[0],              type: v.split(",")?.[1],            });          }          hiveDatas.map((ele) => {            if (              this.attrItem.typeValid &&              this.attrItem.typeValid.includes(ele.type)            ) {              ele.disabled = true;            } else if (!this.attrItem.typeValid) {              ele.disabled = true;            } else {              ele.disabled = true;            }          });          this.maxHeight =            hiveDatas.length < 10 ? (hiveDatas.length + 1) * 48 : 480;          this.$refs.xTable.options("reloadData", hiveDatas);        } catch (e) {          this.$refs.xTable.options("reloadData", []);        } finally {        }      },    },    components: {      VxeTableCus: () => import("./assets/it-uiue/components/virtualTable/vxe-table"),    },  };</script>

5.文档api连接

https://xuliangzhan\_admin.gitee.io/vxe-table/#/table/start/use