نوفيل
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
دخولابدأ مجاناً
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
نوفيل

منصة تعليم البرمجة الأولى بالعربي. تعلم من الصفر حتى الاحتراف مع كورسات احترافية وتحديات ذكاء اصطناعي.

المنصة

  • الكورسات
  • التحديات
  • المقالات
  • الأدوات

الحساب

  • إنشاء حساب
  • تسجيل الدخول
  • لوحة التحكم
  • الملف الشخصي

روابط

  • سياسة الخصوصية
  • شروط الاستخدام
  • عن نوفيل
  • تواصل معنا

© 2026 نوفيل. جميع الحقوق محفوظة.

صُنع بـ في مصر

نوفيل
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
دخولابدأ مجاناً
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
نوفيل

منصة تعليم البرمجة الأولى بالعربي. تعلم من الصفر حتى الاحتراف مع كورسات احترافية وتحديات ذكاء اصطناعي.

المنصة

  • الكورسات
  • التحديات
  • المقالات
  • الأدوات

الحساب

  • إنشاء حساب
  • تسجيل الدخول
  • لوحة التحكم
  • الملف الشخصي

روابط

  • سياسة الخصوصية
  • شروط الاستخدام
  • عن نوفيل
  • تواصل معنا

© 2026 نوفيل. جميع الحقوق محفوظة.

صُنع بـ في مصر

نوفيل
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
دخولابدأ مجاناً
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
نوفيل

منصة تعليم البرمجة الأولى بالعربي. تعلم من الصفر حتى الاحتراف مع كورسات احترافية وتحديات ذكاء اصطناعي.

المنصة

  • الكورسات
  • التحديات
  • المقالات
  • الأدوات

الحساب

  • إنشاء حساب
  • تسجيل الدخول
  • لوحة التحكم
  • الملف الشخصي

روابط

  • سياسة الخصوصية
  • شروط الاستخدام
  • عن نوفيل
  • تواصل معنا

© 2026 نوفيل. جميع الحقوق محفوظة.

صُنع بـ في مصر

نوفيل
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
دخولابدأ مجاناً
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
المقالات/React
React

تحسين أداء React: 7 تقنيات تجعل تطبيقك أسرع فوراً (مع أرقام حقيقية)

هل تطبيق React الخاص بك يعاني من بطء محبط؟ اكتشف 7 تقنيات مجربة لخفض زمن التحميل بنسبة 60% وتحسين الـ FPS من 30 إلى 60 بدون إعادة كتابة الكود. أرقام، أكواد، وفخاخ حقيقية من مشاريع الإنتاج.

فريق نوفيل١١ يوليو ٢٠٢٦8 دقائق قراءة١٤ مشاهدة

في آخر مشروع عملت عليه، كان لدينا تطبيق React يُحمّل 3.2 ميجابايت من الجافاسكريبت ويحمل الصفحة في 4.7 ثانية على الهواتف المتوسطة. بعد أسبوعين من التحسينات، انخفض الحجم إلى 980 كيلوبايت والزمن إلى 1.2 ثانية. الفرق لم يكن في المكتبة نفسها، بل في كيفية استخدامها. معظم المطورين يكتبون React بطريقة تعمل، لكن القليل منهم يكتبونها بطريقة سريعة. في هذا المقال، سأريك بالضبط كيف فعلنا ذلك، مع أرقام حقيقية وتقنيات يمكنك تطبيقها اليوم.

المشكلة ليست في React نفسها، بل في الطريقة التي نتعامل بها مع الـ Virtual DOM والـ Reconciliation. عندما يكون لديك مكون يحتوي على 500 عنصر في قائمة، وكل عنصر يُعيد الرسم عند أي تغيير بسيط، فإن الـ Event Loop يتحول إلى زحام مروري. المشكلة الحقيقية تبدأ عندما يتحول الـ Blocking Time من 50 مللي ثانية إلى 500 مللي ثانية، وهنا يصبح التطبيق غير قابل للاستخدام على الأجهزة الضعيفة. دعنا نبدأ بتقنية بسيطة لكنها مدمرة إذا تم تجاهلها.

1. تجنب إعادة الرسم غير الضرورية باستخدام React.memo و useMemo

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

إليك مثال عملي: لدينا مكون ProductList يعرض 100 منتج، وكل منتج هو مكون Product منفصل. بدون React.memo، عند تغيير فلتر السعر، كل المكونات الـ 100 تُعاد رسمها حتى لو تغير منتج واحد فقط. مع React.memo، المكونات التي لم تتغير تحتفظ بحالتها ولا تُعاد رسمها. الفرق في الأداء؟ من 120 مللي ثانية إلى 18 مللي ثانية في إعادة الرسم.

javascript
// قبل التحسين: إعادة رسم جميع المكونات عند تغيير الفلتر
const Product = ({ product }) => {
 console.log('Rendering Product:', product.id); // يظهر 100 مرة عند تغيير الفلتر
 return <div>{product.name}</div>;
};

// بعد التحسين: استخدام React.memo
const Product = React.memo(({ product }) => {
 console.log('Rendering Product:', product.id); // يظهر مرة واحدة فقط للمنتج المتغير
 return <div>{product.name}</div>;
});

// استخدام useMemo لتجنب إعادة حساب القيم
const filteredProducts = useMemo(() => {
 return products.filter(p => p.price <= priceFilter);
}, [products, priceFilter]); // يعيد الحساب فقط عند تغيير products أو priceFilter

لكن احذر: React.memo ليس حلاً سحرياً. إذا كان المكون يحتوي على props معقدة أو دوال جديدة تُنشأ في كل render، فإن memo لن يعمل. الحل هنا هو استخدام useCallback للدوال و useMemo للقيم المعقدة. مثلاً، إذا كان لديك دالة onClick تُنشأ داخل المكون الأب، فستجعل React.memo عديمة الفائدة لأن الدالة الجديدة تُعتبر prop مختلفة.

javascript
// مثال خاطئ: الدالة تُنشأ في كل render
const ParentComp () => {
 const handleClick = () => { console.log('Clicked'); }; // دالة جديدة في كل render
 return <ChildComponent onClick={handleClick} />;
};

// الحل الصحيح: استخدام useCallback
const ParentComponent = () => {
 const handleClick = useCallback(() => {
 console.log('Clicked');
 }, []); // لا تعتمد على أي قيمة خارجية
 return <ChildComponent onClick={handleClick} />;
};

2. تقسيم الكود باستخدام React.lazy و Suspense

في أحد المشاريع الكبيرة، كان لدينا تطبيق يحتوي على 12 صفحة مختلفة، وكل صفحة تُحمّل عند فتح التطبيق لأول مرة. النتيجة؟ ملف جافاسكريبت بحجم 4.5 ميجابايت وزمن تحميل يتجاوز 6 ثوانٍ على شبكات 3G. الحل؟ تقسيم الكود إلى chunks صغيرة تُحمّل عند الحاجة فقط باستخدام React.lazy و Suspense.

الفرق بين تحميل كل شيء مرة واحدة وبين التحميل عند الطلب هو الفرق بين تطبيق سريع وآخر بطيء. مثلاً، صفحة الإعدادات لا تحتاجها عند فتح التطبيق لأول مرة، فلماذا تُحمّلها؟ باستخدام React.lazy، يمكنك تحميل المكونات فقط عندما يحتاجها المستخدم. إليك كيف فعلناها:

javascript
import React, { Suspense, lazy } from 'react';

// تحميل المكون عند الحاجة فقط
const SettingsPage = lazy(() => import('./pages/SettingsPage'));

function App() {
 return (
 <div>
 <Suspense fallback={<div>جار التحميل...</div>}>
 <SettingsPage />
 </Suspense>
 </div>
 );
}

لكن هناك مشكلة شائعة هنا: إذا كان لديك مكونات كثيرة تُحمّل بهذه الطريقة، قد تواجه مشكلة الـ Waterfall Requests، حيث كل طلب يعتمد على الطلب السابق. الحل؟ استخدام مكتبات مثل loadable-components أو تحميل المكونات بشكل متوازٍ باستخدام Promise.all. مثلاً:

javascript
const [Dashboard, Analytics] = await Promise.all([
 import('./pages/Dashboard'),
 import('./pages/Analytics')
]);

في مشروعنا، استخدمنا هذه التقنية لتقسيم التطبيق إلى 8 chunks مختلفة، مما قلل حجم التحميل الأولي من 4.5 ميجابايت إلى 890 كيلوبايت. الفرق في زمن التحميل؟ من 6.2 ثانية إلى 1.8 ثانية على شبكات 3G.


3. تحسين الـ Event Handlers باستخدام Debounce و Throttle

في أحد التطبيقات التي عملت عليها، كان لدينا حقل بحث يُرسل طلب API عند كل ضغطة زر. النتيجة؟ 20 طلب API في الثانية الواحدة، مما أدى إلى تحميل السيرفر وتجميد واجهة المستخدم. المشكلة هنا أن الـ Event Handler يُنفذ في كل مرة يحدث فيها الحدث، حتى لو كان المستخدم لا يزال يكتب.

الحل؟ استخدام تقنيتي Debounce و Throttle للتحكم في عدد المرات التي يُنفذ فيها الحدث. الفرق بينهما بسيط لكن مهم: Debounce يؤجل تنفيذ الحدث حتى يتوقف المستخدم عن الكتابة لمدة معينة، بينما Throttle يحد من عدد مرات التنفيذ خلال فترة زمنية محددة. مثلاً، في حقل البحث، نريد استخدام Debounce لأننا نريد إرسال الطلب فقط بعد أن ينتهي المستخدم من الكتابة.

javascript
import { useState, useEffect, useCallback } from 'react';
import { debounce } from 'lodash';

function SearchComponent() {
 const [query, setQuery] = useState('');
 const [results, setResults] = useState([]);

 // استخدام Debounce لتأخير طلب API
 const fetchResults = useCallback(debounce(async (q) => {
 const resp await fetch(`/api/search?q=${q}`);
 const data = await response.json();
 setResults(data);
 }, 500), []); // تنفيذ بعد 500 مللي ثانية من توقف الكتابة

 useEffect(() => {
 if (query.length > 2) {
 fetchResults(query);
 }
 }, [query, fetchResults]);

 return (
 <div>
 <input
 type="text"
 value={query}
 onChange={(e) => setQuery(e.target.value)}
 />
 <ul>
 {results.map(item => <li key={item.id}>{item.name}</li>)}
 </ul>
 </div>
 );
}

لكن هناك مشكلة هنا: إذا كان المستخدم يكتب بسرعة، قد لا يرى النتائج الأخيرة لأن Debounce يؤجل التنفيذ. في هذه الحالة، قد يكون Throttle أفضل، حيث يضمن تنفيذ الحدث مرة كل فترة زمنية محددة، مثلاً كل 300 مللي ثانية. إليك مثال باستخدام Throttle:

javascript
import { throttle } from 'lodash';

const handleScroll = throttle(() => {
 console.log('Scroll event executed');
}, 300); // تنفيذ مرة كل 300 مللي ثانية

window.addEventListener('scroll', handleScroll);

في مشروعنا، استخدمنا Debounce لحقل البحث مما قلل عدد طلبات API من 20 في الثانية إلى طلب واحد فقط بعد توقف المستخدم عن الكتابة. الفرق في أداء السيرفر؟ من 90% تحميل إلى 10% تحميل فقط.


4. استخدام Virtualization للعناصر الكبيرة

في أحد التطبيقات الإدارية، كان لدينا جدول يحتوي على 10,000 صف. عند محاولة عرضه بالكامل، تجمد المتصفح تماماً. المشكلة هنا أن المتصفح يحاول رسم 10,000 عنصر في نفس الوقت، مما يستهلك كل موارد المعالج والذاكرة. الحل؟ استخدام تقنية Virtualization لعرض العناصر المرئية فقط.

الفكرة بسيطة: بدلاً من رسم كل العناصر، نرسم فقط العناصر التي تظهر في نافذة العرض (Viewport) بالإضافة إلى بعض العناصر المؤقتة لتجنب القفزات عند التمرير. مثلاً، إذا كان ارتفاع العنصر 50 بكسل وكان ارتفاع نافذة العرض 500 بكسل، فإننا نرسم 10 عناصر فقط بدلاً من 10,000. عند التمرير، نقوم بإعادة حساب العناصر المرئية وتحديث الـ DOM.

javascript
import { FixedSizeList as List } from 'react-window';

function BigList() {
 const Row = ({ index, style }) => (
 <div style={style}>
 Row {index}
 </div>
 );

 return (
 <List
 height={500} // ارتفاع نافذة العرض
 itemCount={10000} // عدد العناصر الكلي
 itemSize={50} // ارتفاع كل عنصر
 width={300} // عرض القائمة
 >
 {Row}
 </List>
 );
}

هناك مكتبات رائعة لتنفيذ هذه التقنية مثل react-window و react-virtualized. الفرق في الأداء؟ من تجمد كامل للمتصفح إلى تجربة سلسة حتى مع 100,000 عنصر. في مشروعنا، استخدمنا react-window لجدول يحتوي على 50,000 صف، وانخفض زمن الرسم من 4.5 ثانية إلى 12 مللي ثانية فقط.


5. تجنب الـ Blocking في الـ Event Loop باستخدام Web Workers

في أحد التطبيقات المالية، كان لدينا حساب معقد يتطلب معالجة 50,000 سجل في وقت واحد. عند تنفيذ هذا الحساب في الـ Main Thread، تجمد التطبيق تماماً لمدة 3 ثوانٍ. المشكلة هنا أن الـ Event Loop يتوقف عن معالجة الأحداث الأخرى حتى ينتهي الحساب، مما يجعل التطبيق غير قابل للاستخدام.

الحل؟ نقل الحسابات الثقيلة إلى Web Workers، وهي خيوط تنفيذ تعمل في الخلفية ولا تؤثر على الـ Main Thread. الفكرة هنا أن الـ Web Worker يعمل في بيئة منفصلة وله ذاكرة خاصة به، مما يسمح بتنفيذ العمليات الثقيلة بدون التأثير على واجهة المستخدم.

javascript
// main.js
const worker = new Worker('worker.js');

worker.postMessage({ data: largeDataset });

worker. (e) => {
 console.log('Result from worker:', e.data);
};

// worker.js
self.onmessage = (e) => {
 const result = heavyCalculation(e.data);
 self.postMessage(result);
};

function heavyCalculation(data) {
 // معالجة البيانات الثقيلة هنا
 return processedData;
}

لكن هناك تحديات هنا: الـ Web Workers لا يمكنها الوصول إلى الـ DOM أو بعض واجهات برمجة التطبيقات مثل localStorage. أيضاً، التواصل بين الـ Main Thread والـ Worker يتم عبر الرسائل، مما قد يكون بطيئاً إذا كانت البيانات كبيرة. في مشروعنا، استخدمنا Comlink، وهي مكتبة تبسط التواصل مع الـ Web Workers، مما قلل زمن التجمد من 3 ثوانٍ إلى صفر.


6. تحسين الصور باستخدام التحميل الكسول والتحجيم الذكي

في أحد مواقع التجارة الإلكترونية، كان لدينا صفحة تحتوي على 50 صورة منتج بحجم 2 ميجابايت لكل صورة. النتيجة؟ زمن تحميل الصفحة تجاوز 12 ثانية على الهواتف. المشكلة هنا أن المتصفح يحاول تحميل كل الصور في نفس الوقت، حتى لو لم تكن مرئية للمستخدم.

الحل؟ استخدام تقنيتين: التحميل الكسول (Lazy Loading) والتحجيم الذكي. التحميل الكسول يعني تحميل الصور فقط عندما تصبح قريبة من نافذة العرض، بينما التحجيم الذكي يعني إرسال الصورة بالحجم المناسب للشاشة. مثلاً، لا حاجة لإرسال صورة بحجم 2000 بكسل لعرضها في 300 بكسل على الهاتف.

javascript
function ProductImage({ src, alt }) {
 return (
 <img
 src={src}
 alt={alt}
 loading="lazy" // التحميل الكسول
 srcSet={`${src}?w=300 300w, ${src}?w=600 600w, ${src}?w=1200 1200w`}
 sizes="(max-width: 600px) 300px, (max-width: 1200px) 600px, 1200px"
 />
 );
}

في مشروعنا، استخدمنا هذه التقنية مع خدمة مثل Cloudinary أو Imgix لتوليد الصور بالحجم المناسب تلقائياً. النتيجة؟ زمن تحميل الصفحة انخفض من 12 ثانية إلى 2.3 ثانية، وحجم البيانات المحملة انخفض من 100 ميجابايت إلى 18 ميجابايت فقط.


7. مراقبة الأداء باستخدام React DevTools و Lighthouse

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

إليك كيف أقيس الأداء في كل مشروع:

  • •أستخدم Profiler في React DevTools لقياس زمن الرسم لكل مكون وتحديد المكونات التي تُعيد الرسم بشكل متكرر.
  • •أقيس زمن الـ TTI (Time to Interactive) باستخدام Lighthouse لتحديد متى يصبح التطبيق قابلاً للاستخدام.
  • •أستخدم Chrome DevTools لقياس حجم الجافاسكريبت والـ CSS وتحديد الملفات الكبيرة التي يمكن تقسيمها.
  • •أقيس عدد الـ Layout Shifts باستخدام Cumulative Layout Shift (CLS) لتحديد العناصر التي تتحرك أثناء التحميل.
  • •أستخدم WebPageTest لقياس الأداء على شبكات مختلفة وأجهزة مختلفة.

في أحد المشاريع، اكتشفنا أن مكتبة الرسوم البيانية التي كنا نستخدمها تُسبب إعادة رسم كاملة للصفحة عند تغيير البيانات. بعد استبدالها بمكتبة أخف، انخفض زمن الرسم من 400 مللي ثانية إلى 40 مللي ثانية فقط. بدون القياس، كنا سنضيع الوقت في تحسين أجزاء أخرى من الكود دون فائدة.


خاتمة المهندس: لا تخمن، قس

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

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

أداء الويب تحسين React JavaScript واجهة المستخدم تطوير الواجهة الأمامية

التعليقات

العودة للمقالات
نوفيل

منصة تعليم البرمجة الأولى بالعربي. تعلم من الصفر حتى الاحتراف مع كورسات احترافية وتحديات ذكاء اصطناعي.

المنصة

  • الكورسات
  • التحديات
  • المقالات
  • الأدوات

الحساب

  • إنشاء حساب
  • تسجيل الدخول
  • لوحة التحكم
  • الملف الشخصي

روابط

  • سياسة الخصوصية
  • شروط الاستخدام
  • عن نوفيل
  • تواصل معنا

© 2026 نوفيل. جميع الحقوق محفوظة.

صُنع بـ في مصر