أداء React، باختصار
ركز على عدد أقل من عمليات العرض، وعدد أقل من عقد DOM، وعدد أقل من البايتات، وتأجيل العمل غير الحرج.
قم بالقياس أولاً
- استخدم لوحة الأداء ومحلل React
- تتبع Core Web Vitals (LCP, INP, CLS)
قلل من عمليات إعادة العرض
- استقرار الدعائم مع
useMemo
/useCallback
فقط حيث يظهر التوصيف اضطرابًا - قسم السياق الكبير أو استخدم خطافات التحديد
- فضل واجهة المستخدم المشتقة على تكرار الحالة
اعرض أقل
- افتراض القوائم الطويلة (react-window)
- تقسيم الكود للمسارات/الأدوات الثقيلة (React.lazy + Suspense)
شحن عدد أقل من البايتات
- تحسين الصور (الأحجام/srcset، التنسيقات الحديثة، التحميل الكسول)
- تخزين استجابات API مؤقتًا؛ تأخير المدخلات؛ الضغط عبر الشبكة
حافظ على سرعة التفاعلات
useTransition
/useDeferredValue
للتحديثات المكلفة أثناء الكتابة- تجنب اضطراب التخطيط؛ فضل
transform
/opacity
للرسوم المتحركة
قائمة التحقق: دعائم مستقرة، افتراض القوائم الكبيرة، تحسين الصور، تقسيم الكود، التحقق باستخدام Web Vitals.
const ExpensiveList = memo(({ items }) => {
const sorted = useMemo(() => items.slice().sort(), [items]);
const handleClick = useCallback((id) => console.log(id), []);
return sorted.map((id) => (
<button key={id} onClick={() => handleClick(id)}>
{id}
</button>
));
});
افتراض القوائم الطويلة
استخدم مكتبات النوافذ للقوائم التي تزيد عن 100 عنصر.
تقسيم الكود
قم بالتحميل الكسول للمسارات والمكونات غير الحرجة باستخدام React.lazy
و Suspense
.
تحسين الصور
قدم صورًا سريعة الاستجابة، واستخدم تنسيقات حديثة (WebP/AVIF)، وقم بالتحميل الكسول للصور خارج الشاشة.
قم بالقياس أولاً
قم بالتوصيف باستخدام محلل React DevTools ولوحة أداء المتصفح قبل تغيير الكود.
قم بتوصيف React بالطريقة الصحيحة
ابدأ بعلامة تبويب الأداء ومحلل React:
- سجل التفاعلات التي تبدو بطيئة (تغيير المسار، تصفية القائمة، فتح النوافذ المنبثقة).
- حدد المكونات ذات أوقات العرض الطويلة وعدد الالتزامات المرتفع.
- تحقق من "إعادة العرض المتتالية" حيث يعيد الأصل عرض العديد من الأبناء.
إذا أظهر الرسم البياني للهب العديد من عمليات العرض دون تغييرات في الحالة، فمن المحتمل أن يكون لديك دعائم غير مستقرة أو إعادة رسم للسياق.
منع عمليات إعادة العرض غير الضرورية
- مرر مراجع مستقرة: انقل الكائنات/الدوال المضمنة للخارج أو قم بتغليفها بـ
useMemo
/useCallback
عندما تسبب تكلفة إعادة عرض حقيقية. - قسم السياق: تفرض السياقات الكبيرة إعادة عرض واسعة؛ قسمها حسب الاهتمام أو استخدم السياقات المستندة إلى المحدد.
- فضل واجهة المستخدم المشتقة على الحالة المشتقة: احسب واجهة المستخدم من الحالة الأولية بدلاً من تخزين نسخ متعددة.
// سيء: كائن جديد في كل عرض ← إعادة عرض الابن
<Chart options={{ color: theme.primary }} />;
// جيد: قيمة مذكرة مستقرة
const chartOptions = useMemo(() => ({ color: theme.primary }), [theme.primary]);
<Chart options={chartOptions} />;
React.memo ومتى تستخدمه
يمكن لـ React.memo
تقليل عمليات إعادة عرض المكونات النقية. يساعد أكثر عندما:
- نادرًا ما تتغير الدعائم
- عمل المكون مكلف (التنسيق، DOM الثقيل، القوائم الكبيرة)
احذر من الإفراط في استخدامه - فالتذكر له تكلفة مقارنة. أضفه حيث يظهر التوصيف فائدة.
const Card = React.memo(function Card({ title, children }) {
return (
<section className="blog-card rounded-xl p-6">
<h3 className="font-bold mb-2">{title}</h3>
{children}
</section>
);
});
افتراض القوائم الطويلة حقًا
بالنسبة لـ 100+ عنصر، اعرض فقط ما هو مرئي. تقلل مكتبات مثل react-window
أو react-virtualized
من عقد DOM بشكل كبير.
npm i react-window
import { FixedSizeList as List } from "react-window";
const Row = ({ index, style, data }) => (
<div style={style}>{data[index].title}</div>
);
export function PostsVirtualized({ items }) {
return (
<List
height={480}
itemCount={items.length}
itemSize={56}
width={"100%"}
itemData={items}
>
{Row}
</List>
);
}
تحسين الصور بقوة
- قم دائمًا بتضمين
width
وheight
جوهريين أو نسب الحاوية لتقليل CLS. - استخدم
loading="lazy"
وdecoding="async"
(موجود بالفعل فيLazyImage
لهذا المشروع). - قدم أحجامًا متعددة باستخدام
srcset
/sizes
عندما يكون ذلك ممكنًا.
<img
src="/img/hero-1280.jpg"
srcset="
/img/hero-640.jpg 640w,
/img/hero-960.jpg 960w,
/img/hero-1280.jpg 1280w
"
sizes="(max-width: 768px) 100vw, 768px"
alt="Hero"
loading="lazy"
decoding="async"
/>
تقسيم الكود لواجهة المستخدم غير الحرجة
قم بالتحميل الكسول للأدوات الثقيلة والحزم على مستوى المسار:
const Comments = React.lazy(() => import("./Comments"));
export function PostFooter() {
return (
<React.Suspense fallback={<div>Loading comments…</div>}>
<Comments />
</React.Suspense>
);
}
تعديلات الشبكة ووقت التشغيل
- تخزين استجابات API مؤقتًا؛ تأخير الاستعلامات التي تعتمد على الإدخال.
- تجنب JSON الكبير في الذاكرة؛ قم بالتدفق حيثما أمكن.
- استخدم CDN للأصول الثابتة؛ فضل ضغط Brotli.
ربط التحسينات بـ Web Vitals
تتبع LCP و INP و CLS. يتضمن هذا المشروع بالفعل أداة أداء (src/utils/performance.js
). تأكد من تكوين خطافات التحليلات حتى تتمكن من التحقق من التأثير.
جدولة العمل للحفاظ على سرعة التفاعلات
استخدم الميزات المتزامنة لمنع تجميد واجهة المستخدم أثناء ضغطات المفاتيح والتنقل.
import { useDeferredValue, useTransition } from "react";
export function Search({ list }) {
const [term, setTerm] = React.useState("");
const [isPending, startTransition] = useTransition();
// Defer filtering to keep typing responsive
const deferredTerm = useDeferredValue(term);
const results = React.useMemo(
() => list.filter((x) => x.includes(deferredTerm)),
[list, deferredTerm]
);
function onChange(e) {
const value = e.target.value;
startTransition(() => setTerm(value));
}
return (
<div>
<input value={term} onChange={onChange} />
{isPending && <span>Updating…</span>}
<ul>
{results.map((r) => (
<li key={r}>{r}</li>
))}
</ul>
</div>
);
}
تجنب اضطراب التخطيط
قم بتجميع القراءات والكتابات وتقليل قياسات التخطيط المتزامنة في المسارات الساخنة.
// سيء: يفرض التخطيط على كل حركة الماوس
element.addEventListener("mousemove", () => {
const { left } = element.getBoundingClientRect(); // قراءة
element.style.transform = `translateX(${left}px)`; // كتابة
});
// أفضل: requestAnimationFrame وقراءات مخزنة مؤقتًا
let frame = 0;
element.addEventListener("mousemove", () => {
if (frame) return;
frame = requestAnimationFrame(() => {
const left = cachedLeft; // from a scheduled read
element.style.transform = `translateX(${left}px)`;
frame = 0;
});
});
استخدم CSS حيثما أمكن (مثل transform
، opacity
) وأشر بـ will-change
للعناصر المتحركة للغاية.
حدود جلب البيانات و Suspense
قم بتغليف الأشجار الفرعية البطيئة في Suspense
مع بدائل خفيفة الوزن للحفاظ على تفاعل الصفحة.
const Pricing = React.lazy(() => import("./Pricing"));
export function Page() {
return (
<React.Suspense fallback={<div className="h-32 animate-pulse" />}>
<Pricing />
</React.Suspense>
);
}
مزالق الترطيب
- تجنب قراءة
window
/document
أثناء العرض الأولي؛ قم بالحماية باستخدام التأثيرات أو فحوصات البيئة. - حافظ على اتساق ترميز الخادم والعميل (لا توجد معرفات عشوائية بدون بذور مستقرة).
- قدم
width
/height
صريحين للوسائط لتجنب تحولات التخطيط بعد الترطيب.
قائمة التحقق
- دعائم مستقرة وتذكر للمسارات الساخنة
- قوائم افتراضية للمجموعات الكبيرة
- صور محسنة بأحجام مناسبة وتحميل كسول
- تقسيم الكود الاستراتيجي للمكونات الثقيلة
- مراقبة Web Vitals قبل/بعد التغييرات
الخلاصة
قم بالقياس أولاً، وقم بالتحسين حيث يؤلم، وتحقق باستخدام أدوات التوصيف. تأتي معظم المكاسب من تقليل العمل (العروض، DOM، البايتات) وتأجيل ما لا يحتاجه المستخدم على الفور.