كيفية تقليص حزم لوحات المعلومات المتضخمة باستخدام 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
아직 작성된 글이 없습니다
إذا دخلت صفحة لوحة معلومات وتظهر ساعة الرمل بسبب تحميل الرسوم البيانية، فالسبب بنسبة تسعين بالمائة هو مشكلة في Recharts. رؤية حجم الحزمة يتضخم بمقدار مئات الكيلوبايتات لمجرد إضافة بضع رسوم بيانية تجعل المرء يتنهد. ترتبط مكتبة Recharts ارتباطاً وثيقاً بدورة ضبط DOM في React، مما يجعل بنية واجهة المستخدم معالجة البيانات متداخلة بشكل يصعب معه استخدامها بخفة.
في مثل هذه الحالات، تعد TanStack Charts بديلاً مناسباً. فقد تم فصل منطق التجسيد (Visualization) في نواة مستقلة مبنية على قواعد الرسوم (Grammar of Graphics). بينما تستهلك Recharts عادةً أكثر من 150 كيلوبايت، فإن TanStack Charts لا تتجاوز 40 كيلوبايت تقريباً، مما يقلل من حجم الحزمة بنسبة يقارب 70%.
تكمن المشكلة في ترحيل كود الإنتاج (Production). فالتلاعب بشاشة تعمل بشكل جيد يثير القلق دائماً. ولإقناع أعضاء الفريق والانتقال بسلاسة دون أخطاء، يلزم وجود تسلسل واضح.
محاولة تغيير كل شيء دفعة واحدة تجعل من الصعب اكتشاف مصدر الأعطال. يجب اختيار مكون واحد وتفكيكه إلى 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، مما يمنع خيط المتصفح (Thread) من التحمل، وتظهر التقطيعات بوضوح للعين.
الحل يكمن في دمج أمرين: التحول إلى عارض 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;
}
`
عند التعاملกับ السلاسل الزمنية الكبيرة، استخدم LTTB لتقليص حجم البيانات إلى حوالي 1000 نقطة. بعد ذلك، قم بتفعيل خيار spatialIndex: true لتقليل تعقيد اكتشاف مؤشر الماوس. وأخيراً، أضف عارض Canvas من حزمة @tanstack/charts/react/canvas لتحقيق معدل 60 إطاراً في الثانية (60fps).
التنسيقات التي تحتوي على وسوم JSX متداخلة بعمق مثل Recharts غالباً ما تتسبب في هلوسة المساعد الذكي، حيث يولد خصائص غريبة أو يفشل في إغلاق الوسوم.
أما المكتبات التي تعتمد على صيغة إعلان الكائنات مثل TanStack Charts، فيمكنها إنتاج كود نظيف من قِبل الذكاء الاصطناعي شريطة تزويده بمخطط الأنواع (Type Schema) بشكل صحيح. قم بتعريف نوع مخطط مثل أدناه في مشروعك وضعه في توجيهات الذكاء الاصطناعي أو ملف القواعد (.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 في موجه (Prompt) الذكاء الاصطناعي، يمكنك استخدام الكود الذي يخرجه الذكاء الاصطناعي مباشرة دون أي تعديلات.