前言基于之前开发的支持表单验证的el-table,当数据量过大时,渲染会很慢,表单会freeze等致命问题,而且element-ui的el-table本身并没有像antd一样提供虚拟列表的demo和相关支持,所以本文会在之前开发的基础上继续开发虚拟列表.这次分为普通链表和树链表两种。树形有一些对普通链表的考虑,比如扩展和收缩。虚拟列表的简单概述就是滚动和分页,通过有限的视口切片大量的数据,因为渲染相对于js操作是一个非常慢的过程,所以通过一定的js计算,保证较少的数据渲染,往往对于一个更好的用户体验。一般虚拟列表可以通过上下动态padding值在滚动区域显示当前切片的数据,通过transform方法动态移动可见区域。transform方式理论上性能更好,因为浏览器渲染本身是分层渲染的,transform操作的view会被浏览器单独分层,渲染性能更好。下图有两种方式:el-table上的virtuallist这次采用padding方案,因为transform会混淆el-table的样式,如果是自研表或者其他插件有了更好的魔法修改支持,则优先考虑转换。普通列表先看el-table渲染300条的速度。这次有300个测试代码,不算多,但是8列都是slot渲染的表单组件,所以渲染速度慢很多,需要6s+。(antd的表格渲染速度更快,后面会解释原因)提高虚拟列表的渲染速度后:开发过程step1计算总高度height=list.length*65//height为列表的实际总高度//65为每行的行高,根据实际修改//list为实际数据长度计算上下padding值step2paddingTop=scrollTop+"px";paddingBottom=height-10*65-scrollTop+"px";//scrollTop为滚动高度,即列表向下滚动的距离//height总高度//10为实际渲染的条数。step3监听列表滚动,动态设置列表的padding等样式。mounted(){console.time("render300时间:");this.form.rows=newArray(300).fill(0).map((v,i)=>({name:i,children:[]}));this.form.rows=[...this.form.rows];this.setIndex(this.form.rows);这个.calcList();this.$nextTick(()=>{this.debounceFn=_.debounce(()=>{this.scrollTop=this.$refs.table.bodyWrapper.scrollTop;},100);this.$refs.table。bodyWrapper.addEventListener("滚动",this.debounceFn);});this.$nextTick(()=>{console.timeEnd("render300time:");});},监控的目标是这样的:this.$refs.table.bodyWrapper,防抖时间设置为100。step4对数组进行切片,渲染出一个虚拟列表。this.startIndex=Math.floor(scrollTop/65);this.virtualRows=this.form.rows.slice(this.startIndex,this.startIndex+10);根据滚动位置计算出数组切片的起始点,然后截取相应的列表渲染。Supportcolumnfixed(Table-columnAttributes-fixed)上面说了el-table比antd的table渲染慢。我个人认为的原因之一是el-table在支持左右固定列时会克隆一个表。然后根据层级关系,将左右两列固定在UI上。如果左右都设置fixed,那么页面上会同时出现三个表格。Antd的table组件在left和right固定的情况下不会出现这个问题,所以我个人测试了300条相同数据的情况下Antd的性能要好很多。言归正传,解决fixed问题,有必要对这三个表的padding进行一次设置,否则会出现部分区域没有下推错位的情况。让mainTable=this.$refs.table.$el.getElementsByClassName("el-table__body");Array.from(mainTable).forEach(v=>{v.style.height=height+"px";if(this.startIndex+10>=this.num){//因为el-table会晃动滚动到最后,所以增加判断,单独设置属性v.style.paddingTop=scrollTop-65+"px";v.style.paddingBottom=0;}else{v.style.paddingTop=scrollTop+"px";v.style.paddingBottom=height-10*65-scrollTop+"px";}});查找当前表Region下的所有内容,遍历设置样式属性。树列表由于树列表的一步展开和折叠操作,以及其自身的数据结构,数据预处理比较复杂。不能直接切片,而是要计算出对应的区间,然后生成新的数组。其次,收缩后的子项并没有渲染到表中,所以应该排除收缩后的项。除了普通列表的几个步骤外,树列表还需要以下操作。通过滚动数组切片计算出的起始点和可见区域的链表长度可以得到一个区间,比如[3,11],即通过深度优先遍历(也是树链表的顺序),找到第3到第11个数据(不包括折叠项),然后分配给一个新数组。clacTree(){让计数=0;this.virtualRows=[];这个.listLen=0;constfn=arr=>{for(leti=0;i
