تحسين أداء React: useMemo و useCallback و Code Splitting (2024)
تحسين أداء React مع useMemo و useCallback و code splitting. مقاييس أداء حقيقية وأنماط من تطبيقات إنتاجية واسعة النطاق.
مقدمة
كل مطور React يعرف عن useMemo و useCallback و React.memo. لكن عندما ينمو تطبيقك للتعامل مع آلاف العناصر والتحديثات المعقدة في الوقت الفعلي ومتطلبات المؤسسات المتطلبة، هذه الأساسيات ليست كافية.
تغطي هذه المقالة تقنيات التحسين المتقدمة التي طبقتها في لوحات معلومات الرعاية الصحية والبوابات الحكومية وتطبيقات المؤسسات حيث يؤثر الأداء مباشرة على إنتاجية المستخدم.
فهم سلوك React في إعادة التصيير
قبل التحسين، افهم لماذا يعيد React التصيير:
// كل تغيير في الحالة يؤدي إلى تصيير هذا المكون وجميع الأبناء function ParentComponent() { const [count, setCount] = useState(0); const [user, setUser] = useState<User | null>(null); // المشكلة: ExpensiveChild يُعاد تصييره عند تغيير count، // رغم أنه يعتمد فقط على user return ( <div> <button onClick={() => setCount(c => c + 1)}>العدد: {count}</button> <ExpensiveChild user={user} /> </div> ); }
التقنية 1: تركيب المكونات بدلاً من الحفظ
غالباً، إعادة هيكلة المكونات تلغي الحاجة للحفظ:
// ❌ النهج الشائع: حفظ الابن const MemoizedExpensiveChild = React.memo(ExpensiveChild); // ✅ نهج أفضل: إنزال الحالة أو التركيب function ParentComponent() { const [user, setUser] = useState<User | null>(null); return ( <div> <Counter /> {/* حالة العد تعيش هنا الآن */} <ExpensiveChild user={user} /> </div> ); } function Counter() { const [count, setCount] = useState(0); return <button onClick={() => setCount(c => c + 1)}>العدد: {count}</button>; }
التقنية 2: الافتراضية للقوائم الكبيرة
تصيير آلاف العناصر قاتل أداء شائع:
import { useVirtual } from '@tanstack/react-virtual'; function VirtualizedList({ items }: { items: DataItem[] }) { const parentRef = useRef<HTMLDivElement>(null); const virtualizer = useVirtual({ size: items.length, parentRef, estimateSize: useCallback(() => 50, []), overscan: 5, // تصيير 5 عناصر إضافية فوق/تحت منفذ العرض }); return ( <div ref={parentRef} style={{ height: '600px', overflow: 'auto' }}> <div style={{ height: virtualizer.totalSize, position: 'relative' }}> {virtualizer.virtualItems.map(virtualItem => ( <div key={virtualItem.key} style={{ position: 'absolute', transform: `translateY(${virtualItem.start}px)`, }} > <ListItem item={items[virtualItem.index]} /> </div> ))} </div> </div> ); }
التقنية 3: توطين الحالة
احتفظ بالحالة قريبة قدر الإمكان من حيث تُستخدم:
// ❌ حالة عامة لكل شيء const useGlobalStore = create<GlobalState>((set) => ({ user: null, theme: 'light', sidebarOpen: false, // ... 20 خاصية إضافية })); // ✅ التقسيم إلى مخازن/سياقات مركزة const useAuthStore = create<AuthState>((set) => ({ user: null, login: async (credentials) => { /* ... */ }, logout: () => set({ user: null }), })); // المكون يشترك فقط فيما يحتاجه function Sidebar() { const sidebarOpen = useUIStore(s => s.sidebarOpen); // يُعاد تصييره فقط عند تغيير sidebarOpen }
التقنية 4: التحديثات المتفائلة
لا تجعل المستخدمين ينتظرون استجابات الخادم:
function TodoList() { const [todos, setTodos] = useState<Todo[]>([]); const { mutate: toggleTodo } = useOptimisticMutation( (todo: Todo) => api.updateTodo({ ...todo, completed: !todo.completed }), { onMutate: (todo) => { const previous = todos; setTodos(todos.map(t => t.id === todo.id ? { ...t, completed: !t.completed } : t )); return previous; }, onError: (error, todo, previousTodos) => { if (previousTodos) setTodos(previousTodos); toast.error('فشل تحديث المهمة'); }, } ); }
التقنية 5: تقسيم الكود والتحميل الكسول
حمّل الكود فقط عند الحاجة:
// التقسيم على أساس المسار const Dashboard = lazy(() => import('./pages/Dashboard')); const Reports = lazy(() => import('./pages/Reports')); function App() { return ( <Suspense fallback={<PageLoader />}> <Routes> <Route path="/dashboard" element={<Dashboard />} /> <Route path="/reports" element={<Reports />} /> </Routes> </Suspense> ); }
التقنية 6: التأخير والخنق
تحكم في معدل العمليات المكلفة:
function useDebounce<T>(value: T, delay: number): T { const [debouncedValue, setDebouncedValue] = useState(value); useEffect(() => { const timer = setTimeout(() => setDebouncedValue(value), delay); return () => clearTimeout(timer); }, [value, delay]); return debouncedValue; } function SearchComponent() { const [query, setQuery] = useState(''); const debouncedQuery = useDebounce(query, 300); // استدعاء API يُطلق فقط بعد توقف المستخدم عن الكتابة لـ 300ms const { data } = useQuery( ['search', debouncedQuery], () => searchApi(debouncedQuery), { enabled: debouncedQuery.length > 2 } ); }
التنميط والقياس
لا تحسّن عشوائياً - قس أولاً، حدد الاختناقات، ثم طبّق التقنية المناسبة.
الخلاصة
تحسين الأداء يتعلق باتخاذ مقايضات مدروسة:
- أعد هيكلة المكونات قبل إضافة الحفظ
- افترض القوائم الكبيرة بدلاً من تحسين العناصر الفردية
- وطّن الحالة لتقليل نطاق إعادة التصيير
- استخدم التحديثات المتفائلة لأداء محسوس أفضل
- قسّم الكود لتقليل حجم الحزمة الأولية
- أخّر/اخنق العمليات المكلفة
أفضل تحسين غالباً هو الذي لا تحتاجه - الكود البسيط والمنظم عادةً سريع بما فيه الكفاية.
مقالات ذات صلة
هندسة الواجهاتقراءة 17 دقيقة
إدارة الحالة في React: مقارنة Redux مقابل Zustand مقابل Context API
قارن حلول إدارة الحالة في React: Redux Toolkit و Zustand و Context API. تعلم متى تستخدم كل منها مع أمثلة حقيقية واعتبارات الأداء.
الأداء وتحسين محركات البحثقراءة 21 دقيقة
كيفية تحسين Core Web Vitals: دليل تحسين LCP و FID و CLS
تحسين درجات Core Web Vitals مع تقنيات تحسين LCP و FID و CLS العملية. مقاييس قبل/بعد حقيقية من تطبيقات React و Next.js الإنتاجية.
هندسة الواجهاتقراءة 20 دقيقة
بنية Micro-Frontends: تنفيذ Module Federation و Single-SPA
بناء micro-frontends مع Webpack Module Federation و Single-SPA. يغطي تنظيم الفريق والتبعيات المشتركة وأنماط النشر الإنتاجي.
هندسة الواجهاتقراءة 18 دقيقة
أنماط TypeScript المتقدمة: Generics و Utility Types و Type Guards
أتقن أنماط TypeScript المتقدمة بما في ذلك generics و utility types و type guards. أمثلة حقيقية من قواعد كود React و Node.js واسعة النطاق.
الأداء وتحسين محركات البحثقراءة 18 دقيقة
ميزانيات الأداء: تحديد حدود حجم الحزمة مع Webpack و Lighthouse
تحديد وفرض ميزانيات الأداء مع Webpack و Lighthouse CI ومحللات الحزم. يغطي الاختبار الآلي وتكامل CI/CD ومساءلة الفريق.