الوحدة 5 · الإطلاق
تصحيح الأخطاء والأداء
18 دقراءة 5 داكتشف الخطأ
الدرس في لمحة
اعثر على أخطاء Trailhead وشاشاته البطيئة وأصلحها باستخدام LogBox وReact Native DevTools وPerformance Monitor والـ Profiler، مع القياس في وضع الإنتاج قبل تغيير الكود.
- 1الأدوات التي لديك
- 2قِس في وضع الإنتاج
- 3الـ JS thread أم الـ UI thread؟
- 4الإصلاحات، بترتيب العائد
- 5روتين لتصحيح الأخطاء يعمل
لا فيديو هنا، بل الشيء الحقيقي: درس مكتوب وأمثلة قابلة للتشغيل واختبار وتدريب.
هناك خلل ما في Trailhead: السجلّ يتمرّر بقفزات، وزرّ ما أحيانًا لا يفعل شيئًا، ومرة في الأسبوع يُغلَق التطبيق وحده لدى أحد المستخدمين. تصحيح الأخطاء على الجوّال يتبع العادات نفسها التي على الويب: أعِد إنتاج المشكلة، ولاحظ، وكوّن فرضية، وقِس. الأدوات مختلفة، وواحدة منها، أي build الإصدار، أهمّ مما يتوقّعه الناس.
الأدوات التي لديك
LogBox هي الطبقة الظاهرة داخل التطبيق. الخطأ الأحمر بملء الشاشة استثناء JavaScript لم يُلتقط، مع stack trace يشير إلى مصدر الكود. والتحذيرات تظهر شريطًا صغيرًا في الأسفل. أصلح التحذيرات فور ظهورها؛ ومن العادات الجيدة في الفرق أن يُعامَل أي تحذير جديد كأنه اختبار فاشل، لأن التحذيرات التي تتجاهلها هي نفسها التي تتحوّل إلى انهيارات.
مخرجات console.log تظهر في الطرفية التي تشغّل Metro وفي console الـ DevTools. جيدة للفحوص السريعة؛ لكن أزلها من المسارات الساخنة مثل renderItem، حيث يبطئ تسجيل كل صفّ القائمةَ نفسها.
React Native DevTools هو أداة التصحيح المدمجة، مبني على Chrome DevTools. اضغط j في طرفية Metro، أو اختر Open DevTools من قائمة التطوير (m في الطرفية، أو هزّ الجهاز). وفيه:
- Console متصل بتطبيقك العامل،
- Sources مع نقاط التوقّف والتنفيذ خطوة بخطوة وتعابير المراقبة (
Cmd+P/Ctrl+Pللبحث عن ملف)، - مستكشف Components، لرؤية شجرة الـ components والـ props والـ state والـ hooks،
- الـ Profiler، لتسجيل عمليات الـ render ومعرفة ما الذي رُسم ولماذا.
نقطة توقّف واحدة أفضل من عشرة استدعاءات console.log حين لا تعرف أين الخطأ. ضع واحدة في معالج الزر الذي «أحيانًا لا يفعل شيئًا»، وانقر عليه، وافحص الـ state التي لديك فعلًا.
الانهيارات الأصلية (native crashes)، حين يُغلَق التطبيق دون شاشة حمراء، تأتي من الكود الأصلي (مكتبة، أو إذن مضبوط بشكل خاطئ، أو نفاد الذاكرة). أدوات JavaScript لا تراها. اقرأ سجلّات الجهاز: console في Xcode لـ iOS، وLogcat في Android Studio، أو مخرجات الطرفية من npx expo run:android. وفي بيئة الإنتاج، تجمع خدمة تقارير أخطاء مثل Sentry انهيارات JavaScript والانهيارات الأصلية معًا من المستخدمين الحقيقيين، مع stack traces.
قِس في وضع الإنتاج
يشغّل وضع التطوير فحوصًا إضافية: التحقّق من الـ props، والتحذيرات، وقائمة التطوير، وFast Refresh. ويمكن أن يجعل JavaScript أبطأ بعدّة أضعاف. الحكم على الأداء هناك يشبه توقيت عدّاء يحمل حقيبة ظهر. قبل أن تغيّر أي كود من أجل السرعة، شغّل التطبيق دون وضع التطوير:
npx expo start --no-dev --minifyأو ثبّت build إصدار (ستصنع واحدًا باستخدام EAS لاحقًا في هذا القسم). اختبر على هاتف Android متوسط الفئة؛ فإن كان سلسًا هناك، فهو سلس في معظم الأماكن.
الـ JS thread أم الـ UI thread؟
افتح قائمة التطوير وفعّل Performance Monitor. يعرض معدّلَي إطارات، ويقابلان الـ threads الاثنين من الدرس الأول تمامًا:
| العَرَض | السبب المرجّح | أين تبحث |
|---|---|---|
| ينخفض JS FPS ويبقى UI FPS جيدًا | JavaScript أكثر من اللازم: عمليات re-render، وعمل ثقيل في الـ render، وتحليل JSON كبير | الـ Profiler والـ components |
| ينخفض UI FPS | عمل أصلي أكثر من اللازم: أشجار views عميقة، وصور ضخمة، وظلال كثيرة | تسلسل الـ views وأحجام الصور |
| النقرات تبدو متأخّرة | الـ JS thread مشغول حين تصل اللمسة | الـ Profiler أثناء التفاعل |
معظم المشكلات في تطبيقات React Native على جانب JavaScript، ومعظم هذه سببها إعادة الـ render أكثر من اللازم. سجّل جلسة Profiler بينما تعيد إنتاج البطء، ثم ابحث عن components تُرسم أكثر بكثير مما ينبغي. ويستطيع الـ Profiler تسجيل سبب رسم كل component (props تغيّرت، أو state، أو context، أو الأب)، وهذا يحوّل التخمين إلى إصلاح.
الإصلاحات، بترتيب العائد
راجع أولًا قائمة التحقّق من درس القوائم: صفوف مغلّفة بـ memo، وprops ثابتة، وkeyExtractor، وصور بالحجم المناسب. وأبعد من القوائم:
- انقل العمل خارج الـ render. ترتيب 600 رحلة أو تنسيق التواريخ في كل render يتراكم. احسبه مرة واحدة باستخدام
useMemo، أو حين تتغيّر البيانات. - لا تُعِد رسم العالم على مؤقّت. ساعة تدقّ أو موقع GPS حيّ مخزّن في state شاشة كبيرة يعيد الـ render لكل ما تحته. ضع الـ state سريعة التغيّر في أصغر component يعرضها.
- حرّك على الـ UI thread. واجهة
AnimatedمعuseNativeDriver: true، أو مكتبة Reanimated، تشغّل الحركات أصليًا، فلا يستطيع JS thread مشغول أن يجعلها تتقطّع. والإيماءات المقترنة بالحركات (السحب للحذف، والأوراق السفلية) مكانها هناك أيضًا. - دع المُترجِم يتولّى الـ memoization. يدعم Expo الـ React Compiler، الذي يُدرج الـ memoization تلقائيًا. إنه يُلغي فئة كاملة من
memoوuseCallbackالمكتوبة يدويًا، لكنه لا يصلح العمل الثقيل داخل الـ render.
روتين لتصحيح الأخطاء يعمل
حين يصل بلاغ عن خطأ، دوّن الخطوات بالضبط، والجهاز، وإصدار نظام التشغيل. أعِد إنتاج الخطأ على جهاز مشابه. حدّد في أي جانب يقع (خطأ JavaScript، أو انهيار أصلي، أو أداء). استخدم الأداة المناسبة. وحين تصلحه، اسأل نفسك: هل كان يمكن لاختبار أن يلتقطه؟ وهذا موضوع الدرس التالي.
أهم النقاط
- اضغط
jفي طرفية Metro (أو استخدم قائمة التطوير) لفتح React Native DevTools، وفيها console ونقاط توقّف (breakpoints) ومستكشف Components وProfiler. - وضع التطوير أبطأ بكثير من build الإصدار، فاحكم على الأداء باستخدام
npx expo start --no-dev --minifyأو build حقيقي. - يعرض Performance Monitor معدّلَي الإطارات لـ JS وUI منفصلين؛ وأيهما ينخفض يخبرك أين المشكلة.
- معظم البطء في JavaScript داخل React Native سببه عمليات re-render أكثر من اللازم وبتكرار أكثر من اللازم؛ ويُظهر الـ Profiler أي components ولماذا.
- شغّل الحركات على الـ UI thread (عبر Reanimated، أو Animated مع الـ native driver) حتى لا يستطيع JS thread مشغول أن يجعلها تتقطّع.