TuBrief
구독 채널
비디오
커뮤니티

Как уменьшить раздутый бандл дашборда на Recharts с помощью TanStack Charts

TuBrief 편집팀
2026년 8월 12일
0
Computing/Software

원본 영상을 바탕으로 AI의 도움을 받아 작성했습니다. 원본 영상이 기준입니다.

Русский한국어EnglishEspañol中文العربيةहिन्दीDeutschFrançaisPortuguêsBahasa Indonesia日本語

관련 영상

TanStack снова за своё... Они починили графики8:14

TanStack снова за своё... Они починили графики

Better Stack

커뮤니티의 다른 글

사내 시스템에 llm api 붙일 때 마주하는 현실적인 한계와 대응법

2026년 9월 13일

레거시 백엔드에 GPT-6 Astra 붙일 때 예산 승인과 보안 통과를 먼저 끝내는 법이 있습니다

2026년 9월 13일

에이전트끼리 대화하다 6천만 원 청구서가 나오는 이유

2026년 9월 13일

사내 RAG 벡터 검색에 Okta 권한 필터를 직접 거는 방법

2026년 9월 13일

브라우저 에이전트에게 내 구글 계정을 통째로 넘기면 안 되는 이유

2026년 9월 12일

Apple Won the AI Race

2026년 9월 12일

댓글 (0)

Log in to leave a comment

아직 작성된 글이 없습니다

© 2026 . All rights reserved.

TuBrief
구독 채널
비디오
커뮤니티
로그인

Как уменьшить раздутый бандл дашборда на Recharts с помощью TanStack Charts

Если при переходе на страницу дашборда вы видите крутящийся песочные часы из-за загрузки графиков, в 99% случаев дело в Recharts. Когда добавляешь всего пару графиков, размер бандла вырастает на сотни килобайт, и от этого опускаются руки. Recharts слишком тесно привязан к циклу согласования React DOM. Структура пользовательского интерфейса и обработка данных в нем переплетены, поэтому использовать библиотеку легко не получается.

В таких случаях отличной альтернативой становится TanStack Charts. Логика визуализации в ней вынесена в отдельное ядро на основе грамматики графики. В то время как Recharts обычно весит более 150 КиБ, TanStack Charts весит около 40 КиБ. Размер бандла сокращается почти на 70%.

Проблема заключается в миграции продакшн-кода. Ломать то, что отлично работает, всегда волнительно. Чтобы убедить команду и выполнить переход без сбоев, нужен четкий порядок действий.

Безопасное удаление кода Recharts

Пытаться переписать все за один раз — верный способ замучиться с поиском ошибок. Лучше взять один компонент и разбить переход ровно на 4 этапа:

  1. Выделение предварительной обработки данных: Логику подготовки данных, которая была примешана прямо в JSX компонентов Recharts, выносим наружу в виде чистых функций.
  2. Маппинг маркеров: Настройки осей, а также визуальные элементы вроде линий и точек переносим в массив marks объекта декларации defineChart.
  3. Замена утилит: Меняем ResponsiveContainer и Tooltip на автоматический масштабатор от TanStack и модуль @tanstack/charts/tooltip.
  4. Мемоизация: Оборачиваем объект конфигурации графика в 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 }}>


);
}

`

Избегаем повторного рендеринга на JavaScript при переключении темной темы

Перерисовывать весь компонент графика при каждом переключении темной темы — это дурной тон. TanStack Charts из коробки поддерживает CSS-переменные и currentColor вместо хардкодных значений цвета.

Не стоит управлять цветами графиков через состояние JavaScript — лучше передавать их через CSS-переменные. Базовую палитру графиков можно настроить так, чтобы она ссылалась на переменные от --ts-chart-1 до --ts-chart-6.

  1. Заранее определяем переменные для графиков в корне CSS и в селекторе [data-theme='dark'].
  2. Передаем строки вида var(--ts-chart-1) в свойства stroke или fill параметров defineChart.
  3. Стили тултипа также управляем через CSS-классы, а цвет фона задаем переменной.

При таком подходе, если дизайнер попросит изменить цвета графиков, лезть в 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 тысяч точек

Если отрендерить 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.

Как поручить написание кода графиков Cursor или Copilot

При глубокой вложенности тегов 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, код, выдаваемый искусственным интеллектом, можно будет использовать сразу без каких-либо правок.