今天在系统开发中遇到了这么一个问题:
后端一次性返回上百条的数据,我需要把返回的数据全部显示在表格里,而且甲方爸爸明确指定了表格是不允许使用分页的。
可是当使用a-table装载上百条数据时,页面出现了明显的卡顿现象。
这。。。。不允许使用分页的我,只能使用滚动加载的方案来代替了。
实现效果
滚动加载知识储备
参考ant-design-vue中list滚动加载的思路,使用vue-infinite-scroll加上vue-virtual-scroller作为解决方案。
组件封装
因为整个系统使用的框架是ant-dsign-vue,所以组件封装的变量命名风格尽可能的与a-table保持一致。
1. 组件命名
XScrollTable.vue
2. 提供的props
必填字段:
dataSource — 数据源
columns — 表格展示的列信息,插槽用法和a-table不完全一样,下面会提到。
itemSize — 每行数据的高度
选填字段:
rowKey — 数据主键标识,默认为’key’
height — 表格展示区域的高度,默认为500
pageSize — 表格滚动每次滚动加载的数据量,默认为30
infiniteScrollDistance — 表格触发加载的距离条件,默认为10
rowSelection — 表格多选配置,已处理的属性有selectedRowKeys、onChange、width。默认为null,不展示多选。
3.使用举例
首先初始化10000条数据,放在表格中进行显示。
let data = new Array(10000).fill(1);
data = data.map((item1, index) => {
let item = {};
item.id = index;
item.age = "姓名";
item.address = "地址";
return item;
});
export default data;
复制代码
注意:这里之所以加了fill(1),是因为通过Array构造函数产生的数据全是empty,没有数组索引,无法进行map循环。
加载表格
<x-scroll-table
style="margin-top: 10px"
row-key="id"
:itemSize="22"
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange,width:50}"
:columns="columns"
:dataSource="data">
<template slot="action" slot-scope="{record,text}">
<a @click="handleDetail(record)">详情</a>
</template>
</x-scroll-table>
复制代码
组件封装总结
1.尽可能地使用computed计算属性
虽然只是简单地封装了表格,但还是需要定义了很多的属性,使用计算属性代替在data里定义变量,可以减少变量的维护工作量。
整个组件只定义了一个page变量,其它都是使用计算属性的方式。
data() {
return {
// 当前展示页数
page: 1,
};
},
复制代码
举个栗子:
通过page属性定义一个计算属性来表示当前已经加载的数据量
// 展示的最大下标数量,存在比总数据条数多的情况,使用slice解决这个问题
lastIndex() {
return this.pageSize * this.page;
},
复制代码
通过这个计算属性同时衍生出其他的计算属性
// 表示表格数据是否已经加载完毕
busy() {
return this.lastIndex >= this.dataSource.length;
},
// 当前已经加载到RecycleScroller滚动组件的数据
tableData() {
return this.dataSource.slice(0, this.lastIndex);
},
复制代码
通过一个page属性衍生出一系列的计算属性,我只需要维护page属性,其他都是自动计算的。
2.给表格提供插槽
首先通过表格传入的columns参数,计算出需要渲染的列,这里同样使用计算属性。
// 将列数组转为列对象,将columnFieldKey值作为键,数组项作为值
columnMap() {
return this.columns.reduce((returnValue, cur) => {
returnValue[cur[columnFieldKey]] = cur;
return returnValue;
}, {});
},
// 取数组里的列键值--columnFieldKey
columnKeys() {
return this.columns
.map(item => item[columnFieldKey]);
},
复制代码
在template中遍历
<div v-for="(key) of columnKeys"
class="ellipsis-cell"
:key="key"
:style="itemStyle(columnMap[key])"
>
<slot v-if="izSlotRender(columnMap[key])"
:name="columnMap[key].scopedSlots.customRender"
:record="row"
:text="row[key]">
</slot>
<span v-else :title="row[key]">{{ renderItem(row, index, key) }}</span>
</div>
// 是否使用插槽渲染
izSlotRender(item) {
return item.scopedSlots && item.scopedSlots.customRender;
},
复制代码
如果在定义columns时传入了scopedSlots和customRender,将使用插槽渲染。
但是这里存在和ant-design-vue中表格插槽渲染不一样的地方。
我通过slot标签定义的插槽,在父组件获取插槽参数的时候,只能使用slot-scope=”{record,text}”对象解构的方式。而ant-design-vue表格是可以直接使用slot-scope=”record,text”获取参数的。