Как уменьшить раздутый бандл дашборда на Recharts с помощью TanStack Charts
TuBrief 편집팀
2026년 8월 12일
0
Computing/Software원본 영상을 바탕으로 AI의 도움을 받아 작성했습니다. 원본 영상이 기준입니다.
커뮤니티의 다른 글
댓글 (0)
Log in to leave a comment
아직 작성된 글이 없습니다
원본 영상을 바탕으로 AI의 도움을 받아 작성했습니다. 원본 영상이 기준입니다.
Log in to leave a comment
아직 작성된 글이 없습니다
Если при переходе на страницу дашборда вы видите крутящийся песочные часы из-за загрузки графиков, в 99% случаев дело в Recharts. Когда добавляешь всего пару графиков, размер бандла вырастает на сотни килобайт, и от этого опускаются руки. Recharts слишком тесно привязан к циклу согласования React DOM. Структура пользовательского интерфейса и обработка данных в нем переплетены, поэтому использовать библиотеку легко не получается.
В таких случаях отличной альтернативой становится TanStack Charts. Логика визуализации в ней вынесена в отдельное ядро на основе грамматики графики. В то время как Recharts обычно весит более 150 КиБ, TanStack Charts весит около 40 КиБ. Размер бандла сокращается почти на 70%.
Проблема заключается в миграции продакшн-кода. Ломать то, что отлично работает, всегда волнительно. Чтобы убедить команду и выполнить переход без сбоев, нужен четкий порядок действий.
Пытаться переписать все за один раз — верный способ замучиться с поиском ошибок. Лучше взять один компонент и разбить переход ровно на 4 этапа:
marks объекта декларации defineChart.ResponsiveContainer и Tooltip на автоматический масштабатор от TanStack и модуль @tanstack/charts/tooltip.useMemo и передаем его в корневой компонент Chart.Если соблюдать эту последовательность, время на замену первого компонента графика сократится вдвое.
`typescript
import React, { useMemo } from 'react';
import { defineChart, lineY, dot } from '@tanstack/charts';
import { scaleBand } from '@tanstack/charts/scales/band';
import { scaleLinear } from '@tanstack/charts/scales/linear';
import { tooltip } from '@tanstack/charts/tooltip';
import { Chart } from '@tanstack/charts/react';
interface DataRow {
date: string;
value: number;
}
export function MigratedRevenueChart({ data }: { data: DataRow[] }) {
const definition = useMemo(() => {
return defineChart({
marks: [
lineY(data, {
x: 'date',
y: 'value',
stroke: 'var(--ts-chart-1, #2563eb)',
strokeWidth: 2,
}),
dot(data, {
x: 'date',
y: 'value',
fill: 'var(--ts-chart-1, #2563eb)',
r: 4,
}),
],
x: {
scale: () => scaleBand().padding(0.2),
},
y: {
scale: scaleLinear,
nice: true,
grid: true,
},
tooltip: {
use: tooltip,
className: 'custom-tooltip',
},
});
}, [data]);
return (
<div style={{ width: '100%', height: 300 }}>
);
}
`
Перерисовывать весь компонент графика при каждом переключении темной темы — это дурной тон. TanStack Charts из коробки поддерживает CSS-переменные и currentColor вместо хардкодных значений цвета.
Не стоит управлять цветами графиков через состояние JavaScript — лучше передавать их через CSS-переменные. Базовую палитру графиков можно настроить так, чтобы она ссылалась на переменные от --ts-chart-1 до --ts-chart-6.
[data-theme='dark'].var(--ts-chart-1) в свойства stroke или fill параметров defineChart.При таком подходе, если дизайнер попросит изменить цвета графиков, лезть в TSX файлы не придется. Достаточно поправить CSS.
`css
:root {
--bg-chart-card: #ffffff;
--text-chart-main: #172033;
--border-chart-grid: #e2e8f0;
--ts-chart-1: #2563eb;
--ts-chart-2: #f97316;
}
[data-theme='dark'] {
--bg-chart-card: #0f172a;
--text-chart-main: #f8fafc;
--border-chart-grid: #334155;
--ts-chart-1: #60a5fa;
--ts-chart-2: #fb923c;
}
.custom-tooltip {
background-color: var(--bg-chart-card);
color: var(--text-chart-main);
border: 1px solid var(--border-chart-grid);
padding: 8px 12px;
border-radius: 6px;
}
`
Если отрендерить 100 тысяч точек с помощью SVG, будет создано 100 тысяч узлов DOM. Потоки браузера просто не выдержат, и подтормаживания станут заметны визуально.
Решение заключается в комбинации двух подходов: переходе на Canvas-рендерер для устранения затрат на создание DOM-узлов и сжатии избыточно плотных данных с помощью алгоритма LTTB (Largest Triangle Three Buckets), если точек больше, чем ширина экрана в пикселях. LTTB сохраняет ключевые особенности данных (максимумы и минимумы), уменьшая количество точек.
`typescript
export function lttbDownsample(
data: T[],
xAccessor: (d: T) => number,
yAccessor: (d: T) => number,
threshold: number
): T[] {
const dataLength = data.length;
if (threshold >= dataLength || threshold === 0) return data;
const sampled: T[] = [];
let sampledIndex = 0;
const bucketSize = (dataLength - 2) / (threshold - 2);
sampled[sampledIndex++] = data[0];
for (let i = 0; i < threshold - 2; i++) {
let avgX = 0;
let avgY = 0;
const avgRangeStart = Math.floor((i + 1) * bucketSize) + 1;
const avgRangeEnd = Math.floor((i + 2) * bucketSize) + 1;
const currentAvgRangeEnd = avgRangeEnd < dataLength ? avgRangeEnd : dataLength;
for (let j = avgRangeStart; j < currentAvgRangeEnd; j++) {
avgX += xAccessor(data[j]);
avgY += yAccessor(data[j]);
}
avgX /= (currentAvgRangeEnd - avgRangeStart);
avgY /= (currentAvgRangeEnd - avgRangeStart);
const rangeOffs = Math.floor((i + 0) * bucketSize) + 1;
const rangeTo = Math.floor((i + 1) * bucketSize) + 1;
const pointAX = xAccessor(sampled[sampledIndex - 1]);
const pointAY = yAccessor(sampled[sampledIndex - 1]);
let maxArea = -1;
let maxAreaPointIndex = rangeOffs;
for (let j = rangeOffs; j < rangeTo; j++) {
const area = Math.abs(
(pointAX - avgX) * (yAccessor(data[j]) - pointAY) -
(pointAX - xAccessor(data[j])) * (avgY - pointAY)
) * 0.5;
if (area > maxArea) {
maxArea = area;
maxAreaPointIndex = j;
}
}
sampled[sampledIndex++] = data[maxAreaPointIndex];
}
sampled[sampledIndex++] = data[dataLength - 1];
return sampled;
}
`
При работе с большими объемами временных рядов уменьшайте количество точек до уровня 1000 с помощью LTTB. Затем включите опцию spatialIndex: true, чтобы снизить сложность определения положения указателя мыши. Наконец, подключите Canvas-рендерер из пакета @tanstack/charts/react/canvas, и вы получите стабильные 60 fps.
При глубокой вложенности тегов JSX, как в Recharts, AI-ассистенты часто допускают галлюцинации: придумывают несуществующие свойства или забывают закрыть теги.
Библиотеки с декларативным подходом на основе объектов вроде TanStack Charts позволяют AI генерировать чистый код, если им предоставить корректную схему типов. Определите в проекте следующую схему и добавьте ее в промпт для AI или файл правил (.cursorrules):
`typescript
export type ScaleType = 'linear' | 'band' | 'utc' | 'point';
export type MarkType = 'lineY' | 'barY' | 'dot' | 'areaY';
export interface AIScaleConfig {
type: ScaleType;
nice?: boolean;
grid?: boolean;
padding?: number;
}
export interface AIMarkConfig {
type: MarkType;
xKey: string;
yKey: string;
color?: string;
strokeWidth?: number;
}
export interface AIChartGenerationSchema {
data: Record<string, unknown>[];
scales: {
x: AIScaleConfig;
y: AIScaleConfig;
};
marks: AIMarkConfig[];
tooltip?: {
enabled: boolean;
className?: string;
};
}
`
Если прописать в промпте правила импорта из точных подпутей (например, @tanstack/charts/scales/linear) и требования к использованию useMemo, код, выдаваемый искусственным интеллектом, можно будет использовать сразу без каких-либо правок.