Skip to main content

أحدث المقالات

تحسين أداء React: memo وLazy وSuspense والبحث عن الاختناقات الحقيقية

English

Dr. Tarek Barakat

دكتور طارق بركات

مستشار تقني رئيسي، تك فيجن إيرا

معظم مشاريع React في الخليج تبدأ سريعة ثم تتحول إلى ملح — وليس لأن React بطيء، بل لأن المطورين يستخدمون الأدوات الخاطئة بالطريقة الخاطئة. من تجربتي مع أكثر من 50 مشروع، اكتشفت أن 80% من مشاكل الأداء تحل بتغيير بسيط في الفهم، لا في الكود.

memo ليست حل سحري — استخدمها بذكاء لا بالضرورة lazy و Suspense تؤخر تحميل الكود، لكن بشكل يحب الزوار Profiler هي الأداة الوحيدة التي تعرّيك عن مشاكل الأداء الحقيقية
تحسين أداء React: memo وLazy وSuspense والبحث عن الاختناقات الحقيقية

أين تذهب أداء تطبيقك؟

هذا أول سؤال أطرحه على عميل جديد يأتيني يقول: "تطبيقنا بطيء." وتسع مرات من أصل عشر، الإجابة هي: "لا نعرف."

معظم فريق تطوير React في الكويت والخليج لا يستخدم Profiler. هم يكتبون الكود، يتوقعون أن يكون سريعاً، ثم عندما يبدأ المستخدمون يشكون، يتفاجأون. وفي هذه اللحظة، يبدأون ينظرون إلى memo و lazy و Suspense وكأنها حل سحري سينقذهم. لكن هذا ليس الطريق الصحيح.

أنا لست هنا لأخبرك بـ "أفضل الممارسات العامة." أنت سمعتها مئات المرات. أنا هنا لأقول لك الحقيقة: الأداة الحقيقية ليست الكود، هي القياس.

المشكلة ليست في React

دعني أبدأ بحقيقة قد تفاجئك: React نفسه سريع جداً. المحرك الأساسي يعيد تصيير مكون وأطفاله في ميلي ثانية واحدة تقريباً، حتى لو كان المكون معقداً. لكن تطبيقك قد يشعر بالبطء لسبب بسيط: أنت تعيد تصيير أشياء لا تحتاج إلى إعادة تصيير.

تخيل لوحة تحكم لشركة استشارات في الكويت. مئات العملاء والمشاريع والفواتير. زر واحد في الزاوية العلوية يغير اللغة من الإنجليزية إلى العربية. بسيط جداً، أليس كذلك؟

لكن عندما يضغط الزر، ماذا يحدث؟ المكون الأب (GlobalProvider) يتحدث عن اللغة الجديدة. كل مكون فرعي يعاد تصييره — حتى الجداول الضخمة التي لا تتغير. كل صف في الجدول يعاد تصييره — حتى لو كانت معاودة التصيير لا تغير شيئاً مرئياً.

والنتيجة؟ تجميد لمدة ثانية كاملة. ليس لأن React بطيء. بل لأنك تطلب منه إعادة رسم 5000 صف في جدول بدون أي سبب حقيقي.

memo — متى تستخدمها والخطأ الذي تقع فيه كل الفرق

React.memo أداة بسيطة جداً في الواقع: إذا كانت الخاصيات (props) المُمررة إلى مكون لم تتغير، لا تعيد تصييره. نقطة.

الخطأ الكلاسيكي

أنت تحرس المكون بـ memo ثم تمرر دالة جديدة (`onDelete={() => ...}`) في كل عملية تصيير. memo ترى تغيير في الخاصيات (لأن الدالة مرجع جديد) وتقول "سأعيد التصيير على كل حال." memo أصبحت عديمة الفائدة وأنت لا تلاحظ.

رأيت هذا الخطأ بالضبط يُغرق مشاريع كانت ممولة تمويلاً جيداً في السعودية والإمارات. فريق يكتب memo في كل مكان، يشعرون بأنهم "يحسّنون الأداء" بينما هم في الحقيقة يضيفون طبقة معقدة بدون فائدة.

الحل الصحيح هو استخدام `useCallback` لضمان أن الدوال المُمررة تبقى نفسها عبر عمليات التصيير:

```javascript
const ParentList = ({ items, onDeleteItem }) => {
const handleDelete = useCallback((id) => {
onDeleteItem(id);
}, [onDeleteItem]);

return (
<div>
{items.map(item => (
<BlogCard
key={item.id}
data={item}
onDelete={() => handleDelete(item.id)}
/>
))}
</div>
);
};
```

الآن، `handleDelete` تبقى نفسها طالما `onDeleteItem` لم تتغير. أخيراً، memo ستعمل كما توقعت.

لكن دعني أكون صريحاً: معظم الشركات في الكويت لا تحتاج إلى memo في كل مكان. أنت تحتاج إليها عندما يكون عندك مكون ثقيل (شجرة معقدة من الـ children) ويعاد تصييره مئات المرات في الثانية وخاصياته نادراً ما تتغير. إذا كان مكونك بسيطاً، memo قد تضيف مزيداً من التعقيد بدون فائدة حقيقية.

lazy و Suspense — تأخير ذكي، لا كسل

React.lazy تقول لك: "لا تحمل هذا المكون الآن. حمّله عندما يحتاج المستخدم إليه فقط."

مثال واقعي من سوق الخليج: لوحة تحكم مشروع في وكالة تسويق رقمية. تحتوي على ثلاثة أقسام رئيسية — جدول التقارير (ضخم جداً، 2 MB بدون ضغط)، قسم الإعدادات (صغير وبسيط)، ومراقب الأداء (معقد جداً مع مئات الرسوم البيانية).

لماذا تحمل جدول التقارير بكامل حجمه عندما يفتح المستخدم الإعدادات أولاً؟ ستُضيع 2 ثانية إضافية على التحميل الأولي.

```javascript
const Analytics = React.lazy(() => import('./Analytics'));
const Reports = React.lazy(() => import('./Reports'));

const Dashboard = ({ activeTab }) => (
<Suspense fallback={<div>جاري التحميل...</div>}>
{activeTab === 'reports' && <Reports />}
{activeTab === 'analytics' && <Analytics />}
</Suspense>
);
```

الآن عندما يضغط المستخدم على "التقارير" بعد دقائق، يبدأ التحميل في الخلفية. في هذه الأثناء، Suspense تظهر رسالة "جاري التحميل..." بدل شاشة معلقة تماماً.

النتيجة الحقيقية من تطبيق في السعودية استخدم هذا النمط: الصفحة الأولى تحمل في 1 ثانية بدل 4 ثوان. والمستخدم يرى شيئاً يحدث (رسالة التحميل) بدل شاشة معلقة بدون ردود فعل.

Profiler — أداتك الحقيقية للكشف

هنا يأتي الجزء الأهم: القياس. لا الحدس، لا التخمين. القياس الفعلي.

افتح Chrome DevTools. اذهب إلى Components tab. ابحث عن Profiler. اضغط على الزر الأحمر Record. الآن تفاعل مع تطبيقك — غيّر قيمة، اضغط زراً، مرر سهم الماوس. ثم اضغط الزر الأحمر مرة أخرى.

الآن تراني:

أولاً، رسم بياني للتصيير (Render Duration) — كم ميلي ثانية استغرقت عملية التصيير كاملة؟ ثانياً، قائمة المكونات (Component List) — أي مكون استغرق أطول وقت؟ ثالثاً، سبب إعادة التصيير (Why Did This Render) — لماذا أعاد React تصيير هذا المكون بالضبط؟

من تجربتي، هذه الأداة عثرت على الأخطاء التي لا يمكن لأحد أن يخمنها بدون بيانات:

  • مكون صغير يبدو بريئاً (زر "Save") يعيد تصييره 10 مرات في الثانية لأن والده يمرر دالة جديدة في كل مرة.
  • قائمة منسدلة تعاد رسمها بالكامل في كل كبسة مفتاح لأن المطور كتب `filter(items)` داخل الـ render بدل `useMemo`.
  • جدول ضخم يتجمد لأن كل صف يقوم بطلب API — أنت لا تقرأ الأخطاء في الـ console، لكن DevTools ترى 1000 طلب معلق.

تطبيق حقيقي: حالة من الكويت

قبل سنة، أتاني عميل في الكويت — شركة توظيف طورت تطبيقاً ضخماً في React. الشكوى المعتادة: "بطيء جداً. هل نحتاج إلى أداة أفضل؟"

فتحت Profiler. البيانات التي رأيتها:

  • التصيير الواحد يستغرق 850 ميلي ثانية
  • 80% منها في مكون واحد: JobFilters
  • المشكلة الفعلية: كل مرة يكتب المستخدم حرفاً واحداً في حقل البحث، يعاد تصيير جدول 5000 وظيفة بالكامل

الحل لم يكن تعقيداً. كان مجرد فصل الاهتمامات بشكل صحيح.

النتيجة بعد التصحيح؟ من 850 ميلي ثانية إلى 150. السرعة زادت بـ 5 مرات ونصف. ليس من خلال تغيير الأداة أو الطلب من الخادم أن يكون أسرع. مجرد قياس وفهم.

ملاحظة شخصية

حين يأتيني عميل يسأل عن الأداء، أول سؤال أطرحه عليه ليس "هل استخدمت memo؟" بل "هل فتحت Profiler قط؟" التسعة الذين قالوا لا يقفون في الزاوية بخجل. الواحد الذي قال نعم — تلك شركة جيدة تعرف ما تفعل.

الخطوات العملية لتطبيقك

لا تبدأ بـ memo في كل مكان. هذا الترتيب:

الخطوة 1: افتح Profiler

تطبيقك في Development mode (ليس Production — Development mode يعطيك رسائل تحذير مفيدة جداً).

الخطوة 2: سجل تفاعل حقيقي

اكتب نصاً، غيّر فلتراً، مرر الماوس. أي تفاعل يشكو منه المستخدمون.

الخطوة 3: اقرأ النتائج

ما المكون الأبطأ؟ كم مرة أعاد التصيير؟ هل كان هناك سبب معقول؟

الخطوة 4: ثم فقط، عدّل الكود

استخدم memo أو useMemo أو useCallback بناءً على ما رأيت في البيانات، لا بناءً على حدسك.

صورة توضيحية لـ تحسين أداء React: memo وLazy وSuspense والبحث عن الاختناقات  — Tech Vision Era
نظرة متعمقة على تحسين أداء React: memo وLazy وSuspense والبحث عن الاختناقات

الواقع الذي قد لا يعجبك

بصراحة، معظم مشاكل الأداء ليست مشاكل JavaScript على الإطلاق. أنت تحتاج إلى:

قاعدة بيانات أفضل (ليس كود JavaScript أنيق). طلبات API أقل (ليس memo). صور محسّنة بشكل صحيح (ليس Suspense). شبكة أسرع (ليس useCallback).

React Profiler ستخبرك إذا كانت المشكلة في الكود JavaScript فعلاً. إذا كانت ستخبرك بوضوح. إذا لم تكن، قد تكون قاعدة بيانات الخادم بطيئة أو API endpoint معيب أو الصور كبيرة جداً.

الخطوة الحقيقية الأولى؟ قياس. دائماً قياس قبل أن تفترض.

الملخص البسيط

تطبيقك ليس بطيئاً لأن React بطيء. تطبيقك بطيء لأنك تطلب من React إعادة رسم أشياء لا تحتاج إلى إعادة رسم. والطريقة الوحيدة لمعرفة ما هو بالفعل مصدر البطء هي استخدام Profiler.

memo و lazy و Suspense ليست تعقيد ولا سحر. هي أدوات بسيطة عندما تستخدمها في المكان الصحيح بناءً على بيانات حقيقية.

خذ ساعة من وقتك الآن. افتح Profiler. سجل تفاعلاً واحداً بطيئاً. اقرأ النتائج. ستفاجأ كم بسيطة الحل عندما تعرف المشكلة الحقيقية.

شارك هذا المقال واتساب X LinkedIn

إشارات البحث الذكي

الأسئلة الشائعة

ما الفرق بين React.memo و PureComponent؟

React.memo تقارن الـ props فقط. PureComponent تقارن الـ props والـ state معاً. إذا كنت تستخدم Hooks (وهذا ما يفعله الجميع الآن)، استخدم memo. إذا كنت تستخدم class components القديمة، استخدم PureComponent. لكن الحقيقة أن معظم الأكواد الجديدة تستخدم Hooks، لذا memo هي المعيار.

متى أستخدم lazy و Suspense بالضبط؟

استخدمها عندما يكون لديك مكون ثقيل (أكبر من 100KB غير مضغوط) أو لا يحتاجه المستخدم فوراً. أمثلة حقيقية: تقارير معقدة، محررات نصية متقدمة، ألعاب داخل التطبيق. إذا كان مكونك بسيطاً (شريط جانبي، قوائم صغيرة)، lazy قد تضيف تأخير بدون فائدة قابلة للقياس.

كيف أعرف أن عندي مشكلة أداء حقاً؟

لا تفترض. استخدم Profiler أو Lighthouse في Chrome. إذا رأيت Main Thread مشغولاً أكثر من 50 ميلي ثانية متتالياً، لديك مشكلة. إذا كانت التفاعلات تشعر بطيئة لكن Profiler هادئة، المشكلة قد تكون في الشبكة أو الخادم، وليس في الكود.

هل React نفسه بطيء في التطبيقات الكبيرة جداً؟

لا. React يمكنه التعامل مع تطبيقات ضخمة جداً بملايين السطور. المشكلة تكون في القرارات المعمارية: كيف تنظم البيانات، كم عدد المكونات التي تعاد رسمها معاً، كيف تدير الحالة المشتركة. React المحرك نفسه ليس المشكلة — قراراتك هي.

كم مرة يعاد تصيير المكون العادي في ثانية واحدة؟

تعتمد تماماً. إذا كان والده يعاد تصييره كل 16 ميلي ثانية (60 FPS)، المكون يعاد تصييره 60 مرة بدون memo. مع memo (وشروط صحيحة)، قد يحدث مرة واحدة أو صفر مرات. هذا هو الفرق الحقيقي بين تطبيق يشعر سريعاً وآخر معلق.

هل memo تحل كل مشاكل الأداء التي أواجهها؟

لا. memo حل لمشكلة واحدة فقط: إعادة تصيير غير ضرورية. إذا كانت مشكلتك "الخادم بطيء" أو "الصور كبيرة جداً" أو "API endpoint يستغرق 5 ثواني"، memo لن تساعد بتاتاً. استخدم القياس أولاً، ثم اختر الحل الصحيح.

ما أفضل طريقة لتنظيم الحالة في React الحديثة؟

ضع الحالة بالقرب من حيث تُستخدم فعلاً. إذا كانت حالة تؤثر على مكون واحد فقط، ضعها في ذلك المكون. إذا كانت تؤثر على مكونات بعيدة، استخدم Context أو Redux. لكن احذر: عندما تضع كل شيء في Context عالية المستوى، تعاد رسم كل تطبيقك بدون سبب.

كيف أقيس أداء تطبيقي في الإنتاج وليس فقط على جهاز التطوير الخاص بي؟

<a href="https://web.dev/performance/" target="_blank" rel="noopener noreferrer">استخدم Core Web Vitals من Google</a>: First Contentful Paint (FCP)، Largest Contentful Paint (LCP)، Cumulative Layout Shift (CLS)، Interaction to Next Paint (INP). استخدم Lighthouse أو Web Vitals library في كودك. في الإنتاج، المتصفحات أسرع من Development (لأن React في Development بطيئة عن قصد لتعطيك تحذيرات)، لكن الشبكة أبطأ عند المستخدمين الحقيقيين.

القيمة التحريرية

محتوى يبني الثقة والسلطة

كل مقالة مصممة لتعزيز التغطية الموضوعية والربط الداخلي والظهور في جوجل ومحركات البحث الذكية.

93%رضا العملاء
1.5Kمشروع ومهمة مكتملة
3 Minمتوسط سرعة الرد

الخطوة التالية

جاهز لتحويل هذا الحضور إلى عملاء؟

تواصل معنا عبر صفحة الاتصال وسنرد خلال 3 دقائق.