最近接到了一个需求,在 大数据场景下 ,实现一个 表格 并保证浏览器不崩,解法有很多,比如牺牲灵活性采用 canvas 画,或者是为了灵活针对 dom 用 虚拟列表,虚拟滚动 这两板斧去改造表格。 不过这些不是本文的重点,因为 开发 这个组件和相关的伴生业务时间只有不到一周的时间,如果从零开始画一个表格,那就更不可能了。
在社区摸索了一阵后发现 ant-design 在 5.0 版本中实现了 虚拟表格的功能,文中最后一句话感动了我:
既然都这么说了,再不使用就对不起这些 巨人 了。
组件是敲定了,下一步就是怎么把 Table 组件放进去了。
前置背景
如图所示,这是笔者在项目中使用到的工程结构,项目通过 pnpm 进行管理。apps 目录下是业务模块,libs 目录下便是一些公共的组件/方法库,其中的 antd 和 antd-pro 是前端组基于 antd v4 源码的基础上魔改的一个版本,而且因为各种各样问题,也没办法直接升级到 antd v5 ,业务代码能消费的组件直接来源于此 。那么问题来了:如何在不改动项目已有 antd@4.0 组件依赖的情况下,接入 antd@5.0 的 table 。 针对上述问题,笔者进行了如下几个方案的尝试:
方案一:
升级 rc-table 的版本为最新(截止发文是 7.47.1)
但没成功🤡,看了一眼 github,在 4.x-stable 中 table 的依赖也没有对应的修改
方案二
在 libs 目录下再建一个依赖 antd@5 的组件库,只对外暴露 Table 组件
相关组件代码如下:
import type { TableProps} from "antd";
import { ConfigProvider, Table, version } from "antd"
export default (props: TableProps) => {
console.log("version", version);
return <ConfigProvider>
<Table {...props}/>
</ConfigProvider>
}
在执行这个方案中,首先采用的是:业务代码直接 源码级 引入相关组件,结果很明显,业务代码打包出来的 Table 组件依然是采用 antd@4 的版本。 于是只能走 先编译,后引入 的方案,直接梭哈 vite。
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { resolve } from 'path'
export default defineConfig({
plugins: [ react()],
build: {
lib: {
entry: resolve(__dirname, 'src/components/table.tsx'),
name: 'siroi-table',
fileName: 'index',
formats: ['es'],
},
rollupOptions: {
external: ['react', 'react-dom' ],
},
},
})
按照上述配置文件 build 之后,文件大小约为 900kb ,还是比较大的,有空再看看怎么优化。
业务代码引入打包好的组件后,在 css in js 的加持下,也没有出现 v4 、v5 样式冲突的问题
示例如下:
import { useEffect, useState } from 'react';
import HocTable from 'siroi-table';
import { Button } from 'antd';
function createData(num: number = 50) {
return Array.from({ length: num }, () => ({
key: Math.random(),
name: `Name ${Math.floor(Math.random() * 10)}`,
age: Math.ceil(Math.random() * 10),
}));
}
export default function Home() {
const [dataSource, setDataSource] = useState(createData(1000));
return (
<div>
<Button>test</Button>
<HocTable
dataSource={dataSource}
columns={[
{
title: '姓名',
dataIndex: 'name',
},
{
title: '年龄',
dataIndex: 'age',
},
]}
pagination={false}
scroll={{ x: 2000, y: 400 }}
virtual={true}
prefixCls="siroi"
onScroll={(e) => {
const {scrollTop, scrollTopMax} = e.target as unknown as{scrollTop: number, scrollTopMax: number};
if(scrollTop === scrollTopMax) {
console.log('到底了');
setDataSource([...dataSource, ...createData(50)]);
}
}}
/>
</div>
);
}
😃使用体验超预期,上线功能后再分析一波源码,手写优化下🤡