Изучение методов виртуализации на примере Ant Table
Зачем нужны виртуализированные таблицы?
Таблица, также называемая сеткой данных, представляет собой расположение данных в строках и столбцах или, возможно, в более сложной структуре. Это важный строительный блок пользовательского интерфейса. И в React Table, и в Ant Table мы предоставили примеры виртуализированных таблиц. Здесь мы используем Ant Table в качестве примера, чтобы углубиться в тему. Этот принцип можно применить и к другим компонентам таблицы.
Вот как использовать Create React App в качестве основы для создания настольного приложения:
% yarn create react-app react-virtual-data % cd react-virtual-data
Установить два пакета
antd
: Ant Design System — это открытый исходный код для языков дизайна пользовательского интерфейса корпоративного уровня и библиотеки пользовательского интерфейса React, которая включает в себя табличный компонент.unique-names-generator
: Это библиотека, которая генерирует уникальные и запоминающиеся строки имен.
% yarn add antd % yarn add unique-names-generator
Они становятся частью dependencies
в package.json
:
"dependencies": { "antd": "^5.4.4", "unique-names-generator": "^4.7.1" }
Создайте src/dataSource.js
, который определяет столбцы таблицы и данные.
const { uniqueNamesGenerator, names, animals, colors, } = require('unique-names-generator'); // table columns are defined as Name, Age, Gender, Grader, // Favorite Animal, and Favorite Color. export const columns = [ { title: 'Name', dataIndex: 'name', key: 'name', }, { title: 'Age', dataIndex: 'age', key: 'age', }, { title: 'Gender', dataIndex: 'gender', key: 'gender', }, { title: 'Grade', dataIndex: 'grade', key: 'grade', }, { title: 'Favorite Animal', dataIndex: 'animal', key: 'animal', }, { title: 'Favorite Color', dataIndex: 'color', key: 'color', }, ]; // generates row data with a given count export const getData = (count) => { const data = []; for (let i = 0; i < count; i++) { const grade = Math.floor(Math.random() * 11) + 1; data[i] = { key: i, name: uniqueNamesGenerator({ dictionaries: [names], }), age: grade + 5, gender: Math.random() > 0.5 ? 'F' : 'M', grade, animal: uniqueNamesGenerator({ dictionaries: [animals], }), color: uniqueNamesGenerator({ dictionaries: [colors], }), }; } return data; };
Измените src/App.js
, чтобы создать таблицу, используя columns
и getData
из dataSource
, где количество строк таблицы равно 100:
import { Table } from 'antd'; import { columns, getData } from './dataSource'; function App() { return ( <Table columns={columns} pagination={false} dataSource={getData(100)} /> ); } export default App;
Выполняем yarn start
, и видим таблицу с вертикальной полосой прокрутки.
- При наличии 100 строк таблица инициализируется быстро.
- Измените таблицу на 1000 строк, и производительность останется хорошей.
- Измените количество на 10 000, и таблица появится через несколько секунд.
- Измените количество на 100 000, и приложение даже не отвечает.
Независимо от вычислительной мощности и эффективности алгоритма существует ограничение на количество узлов DOM, которые должны быть отображены в течение воспринимаемой продолжительности. Существует метод, известный как работа с окнами (виртуализация), чтобы помочь в этой ситуации. Он отображает только небольшое подмножество данных в любой момент времени, что может значительно сократить время, необходимое для повторного рендеринга компонентов, и количество создаваемых узлов DOM.
Как мы можем виртуализировать таблицы?
Существуют популярные библиотеки виртуализации, такие как react-window
, react-virtualized
и react-virtuoso
. Предыдущая статья предоставила примеры для отображения виртуализированных таблиц с использованием Table
react-virtualized
и TableVirtuoso
react-virtuoso
. Это простые таблицы, ограниченные и ограниченные, и они могут не соответствовать визуальным требованиям для корпоративного проекта.
Скорее всего, у проекта есть своя система дизайна, включая брендированные столы. Общие List
или Grid
могут обеспечить большую гибкость для существующих таблиц. React Table помещает FixedSizeList
непосредственно под tbody
, а Ant Table может использовать VariableSizeGrid
внутри components.body
.
Вот таблица Ant TableProps
, чье дополнительное свойство components
можно использовать для настройки элементов таблицы.
export interface TableProps<RecordType>{ columns?: ColumnsType<RecordType>; pagination?: false | TablePaginationConfig; dataSource?: RcTableProps<RecordType>['data']; components?: TableComponents<RecordType>; ... }
TableComponents
может настроить table
, header
и/или body
.
export interface TableComponents<RecordType> { table?: CustomizeComponent; header?: { wrapper?: CustomizeComponent; row?: CustomizeComponent; cell?: CustomizeComponent; }; body?: CustomizeScrollBody<RecordType> | { wrapper?: CustomizeComponent; row?: CustomizeComponent; cell?: CustomizeComponent; }; }
А CustomizeScrollBody
— это компонент с параметрами необработанной data
и информации о прокрутке (scrollbarSize
, ref
и onScroll
).
export declare type CustomizeScrollBody<RecordType> = (data: readonly RecordType[], info: { scrollbarSize: number; ref: React.Ref<{ scrollLeft: number; }>; onScroll: (info: { currentTarget?: HTMLElement; scrollLeft?: number; }) => void; }) => React.ReactNode;
Помимо общих библиотек виртуализации, существуют специальные библиотеки, созданные для виртуализации таблиц Ant, такие как virtualized-table-for-antd
и virtuallist-antd
.
Мы собираемся изучить пять способов виртуализации таблиц Ant. Мы оцениваем каждую библиотеку по следующим пяти критериям:
- Размер в сжатом виде: сколько дополнительного места
- Емкость данных: количество строк, которые можно загрузить в таблицу с приемлемой производительностью.
- Побочные эффекты: Есть ли нежелательное поведение
- Строка кода: номер строки, включая фактический код, комментарии и интервалы, как приблизительное указание сложности реализации.
- Изменение размера: как виртуализированная таблица реагирует на изменение ширины таблицы
Использование React-Window
react-window
— это набор компонентов React для эффективного рендеринга больших списков и табличных данных. Он отображает часть большого набора данных, достаточного, чтобы заполнить область просмотра:
- Это сокращает работу и время, необходимые для визуализации исходного вида и обработки обновлений.
- Это уменьшает объем памяти, избегая чрезмерного выделения узлов DOM.
Установите пакет, react-window
% yarn add react-window
Он становится частью dependencies
в package.json
"dependencies": { "react-window": "^1.8.9" }
react-window
— небольшая библиотека. Согласно https://bundlephobia.com/
, его размер в сжатом виде составляет 6,4 КБ. По умолчанию react-window
состоит из четырех компонентов: FixedSizeList
, VariableSizeList
, FixedSizeGrid
и VariableSizeGrid
.
react-window
— это официальный пример, предоставленный Ant Design System. Когда components.body
используется с реквизитами рендеринга, каждый столбец должен иметь фиксированное значение width
. Вот почему мы создаем mergedColumns
с указанными значениями width
.
VariableSizeGrid
используется для таблицы строк фиксированного размера, потому что ее columnWidth
— это функция, обеспечивающая гибкость.
Работа с ResizeObserver
не обязательна, но помогает показать, как виртуальная таблица приспосабливается к динамической ширине таблицы. Метод resetAfterIndices
VariableSizeGrid
очищает кэшированные данные, чтобы обеспечить синхронизацию заголовка и тела таблицы при изменении размера.
Следующий измененный src/App.js
показывает, как работает виртуальная таблица с использованием react-window
.
import { useEffect, useRef, useState } from 'react'; import { VariableSizeGrid as Grid } from 'react-window'; import ResizeObserver from 'rc-resize-observer'; import { Table } from 'antd'; import { columns, getData } from './dataSource'; import './App.css'; /** * Create an Ant table that is wrapped inside ResizeObserver * @param {Object} props takes Table props * @returns Table Component */ function VirtualTable(props) { const { columns, scroll } = props; const [tableWidth, setTableWidth] = useState(0); // the column width is set to be the equal size // '+ 100' is a use case to generate a horizontal scroll const width = Math.floor(tableWidth / columns.length)/* + 100 */; // each column is assigned the width prop as required by components.body const mergedColumns = columns.map((column) => ({ ...column, width, })); // a ref of the table const gridRef = useRef(); // clear cached data for all items, starting from column 0 const resetVirtualGrid = () => { gridRef.current?.resetAfterIndices({ columnIndex: 0, shouldForceUpdate: true, }); }; // clear cached data after tableWidth changes useEffect(() => resetVirtualGrid, [tableWidth]); // generate the table body for Ant table const renderVirtualList = ( rawData, { scrollbarSize, /* ref, */ onScroll } ) => { const totalHeight = rawData.length * 54; // cell height is 54 return ( // render each cell for proper width and height <Grid ref={gridRef} columnCount={mergedColumns.length} columnWidth={(index) => { const { width } = mergedColumns[index]; // the last column needs to subtract scrollbar size, if there is a vertical scroll return totalHeight > scroll.y && index === mergedColumns.length - 1 ? width - scrollbarSize - 1 : width; }} height={scroll.y} // 650 rowCount={rawData.length} rowHeight={() => 54} width={tableWidth} // needed to scroll header and body together when there is a horizontal scroll onScroll={({ scrollLeft }) => { onScroll({ scrollLeft, }); }} > {/* use render props to render each cell */} {({ columnIndex, rowIndex, style }) => ( // sets the virtual-table-cell class for styling <div className="virtual-table-cell" style={style}> {/* retrieve the cell data for rowIndex and columnIndex */} {rawData[rowIndex][mergedColumns[columnIndex].dataIndex]} </div> )} </Grid> ); }; return ( // Ant table that is wrapped inside ResizeObserver <ResizeObserver onResize={({ width }) => { // adjust the table width, if the browser resizes setTableWidth(width); }} > <Table {...props} columns={mergedColumns} pagination={false} components={{ body: renderVirtualList, // render the table body }} /> </ResizeObserver> ); } function App() { return ( // create the VirtualTable <VirtualTable columns={columns} // column definition dataSource={getData(100000)} // row data scroll={{ y: 650, // the table height }} /> ); } export default App;
Чтобы сделать таблицу похожей на таблицу Ant, src/App.css
изменяется следующим образом:
.virtual-table-cell { box-sizing: border-box; padding: 16px; border-bottom: 1px solid #e8e8e8; } .ant-table { border-bottom: 2px solid #e8e8e8; }
Выполните yarn start
, и мы увидим, что таблица загружается достаточно быстро для 100 000 строк.
Измените количество на 1 000 000. Таблица отображается через несколько секунд, но она все еще работает.
В приведенном выше коде включите следующую строку:
const width = Math.floor(tableWidth / columns.length) + 100;
Он генерирует горизонтальную и вертикальную прокрутку, и обе прокрутки работают хорошо и эффективно.
Изменение размера отзывчивое и плавное.
Вот оценочная карта react-window
:
- Размер в сжатом виде: 6,4 КБ
- Емкость данных: 1 000 000 строк
- Побочные эффекты: Нет
- Строка кода: 116
- Изменение размера: хорошо работает с
ResizeObserver
, а изменение размера является гибким и плавным.
Использование React-Virtualized
react-virtualized
также представляет собой набор компонентов React для эффективного рендеринга больших списков и табличных данных. Он отображает часть большого набора данных, достаточного для заполнения окна просмотра.
Установите пакет, react-virtualized
% yarn add react-virtualized
Он становится частью dependencies
в package.json
"dependencies": { "react-virtualized": "^9.22.5" }
react-virtualized
является прецедентом react-window
. Он громоздкий с полным функционалом. Согласно https://bundlephobia.com/
, его размер в сжатом виде составляет 27,3 КБ. По умолчанию react-virtualized
имеет пять основных компонентов, Collection
, Grid
, List
, Masonry
и Table
, а также восемь компонентов высокого порядка, которые помогают или украшают основные компоненты, AutoSizer
, ArrowKeyStepper
, CellMeasurer
, ColumnSizer
, InfiniteLoader
, MultiGrid
, ScrollSync
и WindowScroller
.
react-virtualized
похож на react-window
, за исключением нескольких небольших отличий:
- Используйте
recomputeGridSize
для очистки кэшированных данных для всех элементов вместоresetAfterIndices
. itemContent
— средство визуализации строк вместо средства визуализации ячеек.cellRenderer
- реквизит вместоchildren
.
Следующий src/App.js
виртуализирует таблицу Ant, используя Grid
. Он использует ResizeObserver
для настройки ширины таблицы при изменении размера браузера.
import { useEffect, useRef, useState } from 'react'; import { Grid } from 'react-virtualized'; import ResizeObserver from 'rc-resize-observer'; import { Table } from 'antd'; import { columns, getData } from './dataSource'; import './App.css'; /** * Create an Ant table that is wrapped inside ResizeObserver * @param {Object} props takes Table props * @returns Table Component */ function VirtualTable(props) { const { columns, scroll } = props; // a none-zero value to show correct header during refresh const [tableWidth, setTableWidth] = useState(1); // the column width is set to be the equal size // '+ 100' is a use case to generate a horizontal scroll const width = Math.floor(tableWidth / columns.length)/* + 100 */; // each column is assigned the width prop as required by components.body const mergedColumns = columns.map((column) => ({ ...column, width, })); // a ref of the table const gridRef = useRef(); // clear cached data for all items, starting from the top left cell const resetVirtualGrid = () => { gridRef.current?.recomputeGridSize({ columnIndex: 0, rowIndex: 0 }); }; // clear cached data after tableWidth changes useEffect(() => resetVirtualGrid, [tableWidth]); // generate the table body for Ant table const renderVirtualList = ( rawData, { scrollbarSize, /* ref, */ onScroll } ) => { const totalHeight = rawData.length * 54; // cell height is 54 return ( // render each cell for proper width and height <Grid ref={gridRef} // call cell renderer to render each cell cellRenderer={({ columnIndex, rowIndex, style }) => ( <div className="virtual-table-cell" style={style}> {rawData[rowIndex][mergedColumns[columnIndex].dataIndex]} </div> )} columnCount={mergedColumns.length} columnWidth={({ index }) => { const { width } = mergedColumns[index]; // the last column needs to subtract scrollbar size, if there is a vertical scroll return totalHeight > scroll.y && index === mergedColumns.length - 1 ? width - scrollbarSize - 1 : width; }} height={scroll.y} // 650 rowCount={rawData.length} rowHeight={() => 54} width={tableWidth} // needed to scroll header and body together when there is a horizontal scroll onScroll={({ scrollLeft }) => { onScroll({ scrollLeft, }); }} /> ); }; return ( // Ant table that is wrapped inside ResizeObserver <ResizeObserver onResize={({ width }) => { // adjust the table width, if the browser resizes setTableWidth(width); }} > <Table {...props} columns={mergedColumns} pagination={false} components={{ body: renderVirtualList, // render the table body }} /> </ResizeObserver> ); } function App() { return ( // create the VirtualTable <VirtualTable columns={columns} // column definition dataSource={getData(1000000)} // row data scroll={{ y: 650, // the table height }} /> ); } export default App;
Выполнить yarn start
. Загрузка 1 000 000 строк занимает несколько секунд, но все равно работает.
В приведенном выше коде включите следующую строку:
const width = Math.floor(tableWidth / columns.length) + 100;
Он генерирует горизонтальную и вертикальную прокрутку, и обе прокрутки работают хорошо и эффективно.
Изменение размера отзывчивое и плавное.
Вот оценочная карта react-virtualized
:
- Размер в сжатом виде: 27,3 КБ
- Емкость данных: 1 000 000 строк
- Побочные эффекты: Нет
- Строка кода: 111
- Изменение размера: хорошо работает с
ResizeObserver
, изменение размера происходит плавно и плавно.
Использование React-Virtuoso
react-virtuoso
— это набор компонентов React, которые могут отображать огромные наборы данных.
Установите пакет, react-virtuoso
% yarn add react-virtuoso
Он становится частью dependencies
в package.json
"dependencies": { "react-virtuoso": "^4.3.1" }
react-virtuoso
— это библиотека среднего размера. Согласно https://bundlephobia.com/
, его размер в сжатом виде составляет 16,3 КБ. По умолчанию react-window
состоит из четырех основных компонентов: Virtuoso
, GroupedVirtuoso
, TableVirtuoso
и VirtuosoGrid
.
react-virtuoso
похож на react-window
или react-virtualized
, за исключением нескольких отличий:
- Не существует метода, подобного
resetAfterIndices
илиrecomputeGridSize
, который очищает кэшированные данные для всех элементов.gridRef
удаляется вместе с блокомuseEffect
для очистки кэшированных данных приtableWidth
изменениях. useWindowScroll
упрощает код прокрутки (нет необходимости вычитать размер полосы прокрутки).itemContent
используется для рендеринга каждой строки.
Следующий src/App.js
виртуализирует таблицу Ant с помощью TableVirtuoso
. Он использует ResizeObserver
для настройки ширины таблицы при изменении размера браузера.
import { useState } from 'react'; import { TableVirtuoso } from 'react-virtuoso'; import ResizeObserver from 'rc-resize-observer'; import { Table } from 'antd'; import { columns, getData } from './dataSource'; import './App.css'; /** * Create an Ant table that is wrapped inside ResizeObserver * @param {Object} props takes Table props * @returns Table Component */ function VirtualTable(props) { const { columns } = props; const [tableWidth, setTableWidth] = useState(0); // the column width is set to be the equal size // '+ 100' is a use case to generate a horizontal scroll const width = Math.floor(tableWidth / columns.length); /* + 100 */ // each column is assigned the width prop as required by components.body const mergedColumns = columns.map((column) => ({ ...column, width, })); // generate the table body for Ant table const renderVirtualList = ( rawData /* { scrollbarSize, ref, onScroll } */ ) => ( // render each row for proper width, and the height is managed by useWindowScroll <TableVirtuoso data={rawData} useWindowScroll itemContent={(rowIndex, item) => columns.map((col, colIndex) => { const { width } = mergedColumns[colIndex]; return ( <td className="virtual-table-cell" key={col.dataIndex} style={{ minWidth: `${width}px`, height: 35, }} > {item[col.dataIndex]} </td> ); }) } /> ); return ( // Ant table that is wrapped inside ResizeObserver <ResizeObserver onResize={({ width }) => { // adjust the table width, if the browser resizes setTableWidth(width); }} > <Table {...props} columns={mergedColumns} pagination={false} components={{ body: renderVirtualList, // render the table body }} /> </ResizeObserver> ); } function App() { return ( // create the VirtualTable <VirtualTable columns={columns} // column definition dataSource={getData(1000000)} // row data scroll={{ // the table height, required by components.body // even it is not needed with useWindowScroll is set y: 650, }} /> ); } export default App;
Выполнить yarn start
. Загрузка 1 000 000 строк занимает несколько секунд, но все равно работает.
В приведенном выше коде включите следующую строку:
const width = Math.floor(tableWidth / columns.length) + 100;
Он генерирует горизонтальную и вертикальную прокрутку. Прокрутите вправо, и заголовок был обрезан.
Это вызвано тем, что не очищаются кэшированные данные для всех элементов.
Измените src/App.css
, чтобы заставить .ant-table-header
отображать overflow
:
.virtual-table-cell { box-sizing: border-box; padding: 16px; border-bottom: 1px solid #e8e8e8; } .ant-table { border-bottom: 2px solid #e8e8e8; } .ant-table-header { overflow: visible !important; }
Теперь обе прокрутки работают хорошо и эффективно.
Изменение размера отзывчивое и плавное.
Вот оценочная карта react-virtuoso
:
- Размер в сжатом виде: 16,3 КБ
- Емкость данных: 1 000 000 строк
- Побочные эффекты: Нет
- Строка кода: 91
- Изменение размера: работает с
ResizeObserver
, но требуется хак, чтобы заголовок не обрезался. Изменение размера отзывчивое и плавное.
Использование Virtualized-Table-For-Antd
virtualized-table-for-antd
— это компонент виртуализированной таблицы для Ant Design System. Это конкретная реализация без использования какой-либо библиотеки виртуализации. Хотя пакет называется virtualizedtableforantd4
, он работает как для antd 4
, так и для antd 5
.
Установите пакет, virtualizedtableforantd4
% yarn add virtualizedtableforantd4
Он становится частью dependencies
в package.json
"dependencies": { "virtualizedtableforantd4": "^1.3.1" }
Согласно https://snyk.io/advisor/npm-package/virtualizedtableforantd4
, его установленный размер составляет 63,9 КБ.
Следующий src/App.js
вызывает useVT
для создания виртуализированного компонента таблицы, используемого в качестве компонента таблицы Ant. Он использует ResizeObserver
для настройки ширины таблицы при изменении размера браузера.
import { useState } from 'react'; import { useVT } from 'virtualizedtableforantd4'; import ResizeObserver from 'rc-resize-observer'; import { Table } from 'antd'; import { columns, getData } from './dataSource'; import './App.css'; /** * Create an Ant table that is wrapped inside ResizeObserver * @param {Object} props takes Table props * @returns Table Component */ function VirtualTable(props) { const { columns, scroll, dataSource } = props; const [tableWidth, setTableWidth] = useState(0); // create a virtual table with specific height const [vt] = useVT(() => ({ scroll: { y: scroll.y } }), []); const totalHeight = dataSource.length * 54; // cell height is 54 // each column is assigned the width prop // the column width is set to be the equal size, except the last column const width = Math.floor(tableWidth / columns.length); const mergedColumns = columns.map((column, index) => ({ ...column, // width width: totalHeight > scroll.y && index === columns.length - 1 ? Math.max([0, width - 25]) : width, })); return ( // Ant table that is wrapped inside ResizeObserver <ResizeObserver onResize={({ width }) => { // adjust the table width, if the browser resizes setTableWidth(width); }} > <Table {...props} columns={mergedColumns} dataSource={dataSource} pagination={false} components={vt} // render the whole virtual table /> </ResizeObserver> ); } function App() { return ( // create the VirtualTable <VirtualTable columns={columns} // column definition dataSource={getData(250000)} // row data scroll={{ y: 650, // the table height }} /> ); } export default App;
Выполнить yarn start
. Загрузка 250 000 строк занимает несколько секунд. Во время загрузки отображается некоторый промежуточный контент.
При изменении размера браузера реакция медленная.
Однако такой автономный пакет не предназначен для работы с ResizeObserver
.
Избавившись от ResizeObserver
, использовать useVT
довольно просто.
import { useVT } from 'virtualizedtableforantd4'; import { Table } from 'antd'; import { columns, getData } from './dataSource'; import './App.css'; /** * Create an Ant table that is wrapped inside ResizeObserver * @param {Object} props takes Table props * @returns Table Component */ function VirtualTable(props) { const { columns, scroll, dataSource } = props; // create a virtual table with specific height const [vt] = useVT(() => ({ scroll: { y: scroll.y } }), []); return ( <Table {...props} columns={columns} dataSource={dataSource} pagination={false} components={vt} // render the whole virtual table /> ); } function App() { return ( // create the VirtualTable <VirtualTable columns={columns} // column definition dataSource={getData(250000)} // row data scroll={{ y: 650, // the table height }} /> ); } export default App;
Выполнить yarn start
. Загрузка 250 000 строк занимает несколько секунд. Во время загрузки все еще отображается некоторый промежуточный контент.
Тем не менее, изменение размера намного более отзывчиво.
Вот оценочная карта react-virtualized
:
- Установленный размер: 63,9 КБ
- Емкость данных: 250 000 строк
- Побочные эффекты: отображение промежуточного контента
- Строка кода: 40
- Изменение размера: Плохо работает с
ResizeObserver
. Его собственное изменение размера является отзывчивым и плавным.
Использование Virtuallist-Antd
virtuallist-antd
— еще один виртуализированный табличный компонент для Ant Design System. Это конкретная реализация без использования какой-либо библиотеки виртуализации. Этот пакет работает как для antd 4
, так и для antd 5
.
Установите пакет, virtuallist-antd
% yarn add virtuallist-antd
Он становится частью dependencies
в package.json
"dependencies": { "virtuallist-antd": "^0.7.6" }
virtuallist-antd
— небольшая библиотека. Согласно https://bundlephobia.com/
, его размер в сжатом виде составляет 3,6 КБ.
Следующий src/App.js
вызывает VList
для создания виртуализированного компонента таблицы, используемого в качестве компонента таблицы Ant. Он использует ResizeObserver
для настройки ширины таблицы при изменении размера браузера.
import { useMemo, useState } from 'react'; import { VList } from 'virtuallist-antd'; import ResizeObserver from 'rc-resize-observer'; import { Table } from 'antd'; import { columns, getData } from './dataSource'; import './App.css'; /** * Create an Ant table that is wrapped inside ResizeObserver * @param {Object} props takes Table props * @returns Table Component */ function VirtualTable(props) { const { columns, scroll, dataSource } = props; const [tableWidth, setTableWidth] = useState(0); const totalHeight = dataSource.length * 54; // cell height is 54 // each column is assigned the width prop // the column width is set to be the equal size, except the last column const width = Math.floor(tableWidth / columns.length); const mergedColumns = columns.map((column, index) => ({ ...column, // width width: totalHeight > scroll.y && index === columns.length - 1 ? Math.max([0, width - 25]) : width, })); // useMemo improves loading performance const renderVirtualList = useMemo(() => { return VList({ height: scroll.y, }); }, [scroll.y]); return ( // Ant table that is wrapped inside ResizeObserver <ResizeObserver onResize={({ width }) => { // adjust the table width, if the browser resizes setTableWidth(width); }} > <Table {...props} columns={mergedColumns} dataSource={dataSource} pagination={false} components={renderVirtualList} // render the whole virtual table /> </ResizeObserver> ); } function App() { return ( // create the VirtualTable <VirtualTable columns={columns} // column definition dataSource={getData(250000)} // row data scroll={{ y: 650, // the table height }} /> ); } export default App;
Выполнить yarn start
. Загрузка 250 000 строк занимает несколько секунд. Во время загрузки отображается некоторый промежуточный контент.
При изменении размера браузера отзывчивость не плавная.
Однако такой автономный пакет не предназначен для работы с ResizeObserver
.
Избавившись от ResizeObserver
, использовать VList
довольно просто.
import { useMemo } from 'react'; import { VList } from 'virtuallist-antd'; import { Table } from 'antd'; import { columns, getData } from './dataSource'; import './App.css'; /** * Create an Ant table that is wrapped inside ResizeObserver * @param {Object} props takes Table props * @returns Table Component */ function VirtualTable(props) { const { columns, scroll, dataSource } = props; // useMemo improves loading performance const renderVirtualList = useMemo(() => { return VList({ height: scroll.y, }); }, [scroll.y]); return ( <Table {...props} columns={columns} dataSource={dataSource} pagination={false} components={renderVirtualList} // render the whole virtual table /> ); } function App() { return ( // create the VirtualTable <VirtualTable columns={columns} // column definition dataSource={getData(250000)} // row data scroll={{ y: 650, // the table height }} /> ); } export default App;
Выполнить yarn start
. Загрузка 250 000 строк занимает несколько секунд. Во время загрузки все еще отображается некоторый промежуточный контент.
Тем не менее, изменение размера намного более отзывчиво.
Вот оценочная карта virtuallist-antd
:
- Размер в сжатом виде: 3,6 КБ
- Емкость данных: 250 000 строк
- Побочные эффекты: отображение промежуточного контента
- Строка кода: 46
- Изменение размера: Плохо работает с
ResizeObserver
. Его собственное изменение размера является отзывчивым и плавным.
Заключение
Мы рассмотрели методы виртуализации на примере таблицы Ant с популярными библиотеками виртуализации react-window
, react-virtualized
и react-virtuoso
, а также специальными библиотеками, созданными для виртуализации таблиц Ant, virtualized-table-for-antd
и virtuallist-antd
.
Вот окончательные оценочные листы:
окно реакции
- Размер в сжатом виде: 6,4 КБ
- Емкость данных: 1 000 000 строк
- Побочные эффекты: Нет
- Строка кода: 46
- Изменение размера: хорошо работает с
ResizeObserver
, а изменение размера является гибким и плавным.
реактивно-виртуализированный
- Размер в сжатом виде: 27,3 КБ
- Емкость данных: 1 000 000 строк
- Побочные эффекты: Нет
- Строка кода: 111
- Изменение размера: хорошо работает с
ResizeObserver
, а изменение размера является гибким и плавным.
виртуоз реакции
- Размер в сжатом виде: 16,3 КБ
- Емкость данных: 1 000 000 строк
- Побочные эффекты: Нет
- Строка кода: 91
- Изменение размера: работает с
ResizeObserver
, но требуется хак, чтобы показать часть переполнения заголовка таблицы. Изменение размера отзывчивое и плавное.
virtualized-table-for-antd
- Установленный размер: 63,9 КБ
- Емкость данных: 250 000 строк
- Побочные эффекты: отображение промежуточного контента
- Строка кода: 40
- Изменение размера: Плохо работает с
ResizeObserver
. Его собственное изменение размера является отзывчивым и плавным.
виртуальный список-antd
- Размер в сжатом виде: 3,6 КБ
- Емкость данных: 250 000 строк
- Побочные эффекты: отображение промежуточного контента
- Строка кода: 46
- Изменение размера: Плохо работает с
ResizeObserver
. Его собственное изменение размера является отзывчивым и плавным.
Мы показали пять способов улучшить производительность таблицы. react-window
— наш выбор, аналогичный рекомендациям React Table и Ant Table.
Спасибо за прочтение.
Спасибо С. Шрираму и Дургадеви Сирипурапу за работу со мной над продуктами Domino.
Want to Connect? If you are interested, check out my directory of web development articles.