في 2025، لم يعد السؤال أي إطار أفضل للموبايل، بل أيهما ينجو من فخاخ الأداء والذاكرة والـ Event Loop تحت ضغط التطبيقات الحقيقية. غوص تقني في قلب المحركين مع أرقام حقيقية وقصص من أرض المعركة.
في عام 2024، نشرنا في نوفيل تحليلاً سريعاً عن أداء React Native مقابل Flutter، لكن الأرقام التي ظهرت في الربع الأول من 2025 قلبت الطاولة رأساً على عقب. التطبيقات التي تعمل بـ 60 فريم في الثانية على Flutter بدأت تتعثر عند تحميل 500 عنصر في القائمة، بينما React Native الذي كان يعاني من الـ Jank في الإصدارات القديمة أصبح الآن يتفوق في اختبارات الـ CPU Bound بفضل تحسينات جذرية في محرك Hermes. السؤال ليس أي إطار أسهل في التعلم، بل أيهما ينجو عندما يتحول الـ UI من عرض بسيط إلى معركة حقيقية مع الـ Garbage Collector والـ Thread Scheduling.
أنا مهندس سنيور في شركة تطوير تطبيقات مالية تتعامل مع 2 مليون مستخدم يومياً، ورأيت بنفسي كيف أن اختيار الإطار ليس مجرد قرار تقني، بل قرار تجاري يحدد ما إذا كان التطبيق سينجو من الانهيار تحت ضغط الـ Peak Load أم لا. في هذا المقال، لن نتحدث عن الـ Syntax أو الـ Widgets، بل سنغوص في ما يحدث تحت غطاء المحركين: كيف يتعامل كل إطار مع الـ Memory Allocation، وكيف يؤثر الـ Bridge في React Native على الـ Latency، ولماذا يعتبر الـ Skia في Flutter سلاحاً ذا حدين عندما يتعلق الأمر بالـ GPU Rendering.
عندما نتحدث عن أداء التطبيقات، فإن المحرك هو الملك. في React Native، محرك Hermes الذي طورته ميتا أصبح الآن هو الخيار الافتراضي، وهو مصمم خصيصاً لتحسين أداء التطبيقات الكبيرة. Hermes هو محرك جافاسكريبت مخصص للتطبيقات الموبايل، ويعمل على تحويل الكود إلى Bytecode أثناء البناء بدلاً من استخدام الـ JIT (Just-In-Time) مثل V8. هذا يعني أن التطبيق يبدأ بشكل أسرع، ويستهلك ذاكرة أقل، لكن الثمن هو فقدان بعض التحسينات الديناميكية التي يوفرها V8.
على الجانب الآخر، Flutter يستخدم Dart VM الذي يدعم كلاً من الـ JIT أثناء التطوير والـ AOT (Ahead-Of-Time) في الإنتاج. الـ AOT يعني أن الكود يتم تحويله إلى كود آلة أصلي قبل تشغيل التطبيق، مما يؤدي إلى أداء قريب من التطبيقات الأصلية. لكن المشكلة هنا هي حجم التطبيق النهائي، حيث أن الـ AOT يزيد من حجم الـ Binary بشكل ملحوظ. في اختباراتنا، تطبيق Flutter بسيط بحجم 15 ميجابايت مقارنة بـ 8 ميجابايت لتطبيق React Native بنفس الوظائف. هذا الفرق يصبح حاسماً في الأسواق الناشئة حيث كل ميجابايت إضافية تعني فقدان مستخدمين بسبب سرعة الإنترنت المحدودة.
// مثال على كود React Native مع Hermes
// لاحظ كيف أن استخدام Hermes يسمح بتحسينات مثل الـ Inline Caching
const data = Array(10000).fill().map((_, i) => ({ id: i, value: Math.random() * 100 }));
// هذا الكود يعمل بشكل أسرع على Hermes بسبب تحسينات الـ Bytecode
const filtered = data.filter(item => item.value > 50);
const sum = filtered.reduce((acc, item) => acc + item.value, 0);
// لكن إذا استخدمت مكتبة خارجية تعتمد على V8، ستخسر كل هذه التحسينات
import { someHeavyLibrary } from 'some-library'; // قد يسبب تباطؤاً ملحوظاًفي Flutter، الكود المكتوب بـ Dart يتم تحويله مباشرة إلى كود آلة، مما يعني عدم وجود أي وسيط بين الكود والتطبيق النهائي. هذا يعطي Flutter ميزة كبيرة في الأداء، خاصة في التطبيقات التي تعتمد على الرسوميات أو الـ Animations المعقدة. لكن هذه الميزة تأتي مع ثمن: عدم القدرة على استخدام مكتبات جافاسكريبت الخارجية بسهولة، وهو ما قد يكون عائقاً كبيراً للمطورين الذين يعتمدون على النظام البيئي الواسع لـ npm.
أكبر نقطة ضعف في React Native تاريخياً كانت الـ Bridge. الـ Bridge هو الوسيط الذي ينقل البيانات بين الكود المكتوب بـ JavaScript والكود الأصلي المكتوب بـ Java/Kotlin أو Objective-C/Swift. هذا النقل يتطلب عملية تسلسل (Serialization) وتحويل للبيانات، مما يضيف تأخيراً ملحوظاً في التطبيقات الكبيرة. في الإصدارات الحديثة، قامت ميتا بتقديم الـ TurboModules والـ Fabric لإعادة تصميم الـ Bridge بشكل جذري، مما قلل من الـ Latency بشكل كبير، لكنه لم يلغِ الحاجة إلى الـ Bridge تماماً.
في المقابل، Flutter لا يستخدم أي bridge. بدلاً من ذلك، يعتمد على محرك Skia للرسم، وهو محرك رسوميات مفتوح المصدر طورته جوجل ويستخدم في متصفح Chrome. Skia يرسم الـ UI مباشرة على الـ Canvas، مما يعني عدم وجود أي وسيط بين الكود المكتوب بـ Dart والواجهة النهائية. هذا يجعل Flutter أسرع في عرض الـ UI، خاصة في التطبيقات التي تحتوي على قوائم طويلة أو رسوميات معقدة. لكن هذه الميزة تأتي مع تحديات خاصة بها: أي خطأ في الكود قد يؤدي إلى تجميد كامل للتطبيق لأن كل شيء يعمل على نفس الـ Thread.
// مثال على كود Flutter يرسم قائمة معقدة
// لاحظ كيف أن كل شيء يتم على نفس الـ Thread
class ComplexList extends StatelessWidget {
final List<Item> items;
const ComplexList({Key? key, required this.items}) : super(key: key);
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return Card(
child: Column(
children: [
Text(items[index].title),
// هذا الكود قد يسبب Jank إذا كان الـ List كبير جداً
// لأن كل شيء يتم على نفس الـ Thread
FutureBuilder(
future: fetchAdditionalData(items[index].id),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data.toString());
} else {
return CircularProgressIndicator();
}
},
),
],
),
);
},
);
}
}
// الحل: استخدام Isolate لفصل العمليات الثقيلة
Future<String> fetchAdditionalData(int id) async {
// هذه العملية ستعمل على Isolate منفصل
return await compute(_fetchDataInIsolate, id);
}
String _fetchDataInIsolate(int id) {
// محاكاة عملية ثقيلة
return 'Data for $id';
}في React Native، الـ Event Loop يعمل بشكل مشابه لجافاسكريبت في المتصفح. هذا يعني أن أي عملية ثقيلة مثل معالجة البيانات الكبيرة أو الـ Image Processing ستؤدي إلى تجميد الـ UI لأن كل شيء يعمل على نفس الـ Thread. الحل التقليدي هو استخدام مكتبات مثل react-native-workers أو نقل العمليات الثقيلة إلى الكود الأصلي، لكن هذا يضيف تعقيداً ويقلل من ميزة كتابة الكود مرة واحدة وتشغيله على منصتين.
في Flutter، الوضع أفضل قليلاً بفضل دعم الـ Isolates. الـ Isolates هي خيوط تنفيذية مستقلة لا تشارك الذاكرة مع بعضها البعض، مما يعني أنه يمكنك تشغيل عمليات ثقيلة في الخلفية دون التأثير على الـ UI. لكن استخدام الـ Isolates ليس سهلاً دائماً، خاصة للمطورين الجدد، وقد يؤدي إلى مشاكل في إدارة الحالة إذا لم يتم التعامل معها بعناية. في تجربتنا، التطبيقات التي تستخدم الـ Isolates بشكل مكثف قد تعاني من مشاكل في الـ Memory Leak إذا لم يتم إغلاق الـ Isolates بشكل صحيح بعد الانتهاء منها.
الـ Garbage Collector هو أحد أكبر التحديات في أي إطار تطوير موبايل. في React Native، يعتمد الـ Garbage Collector على محرك جافاسكريبت المستخدم (Hermes أو V8). Hermes لديه ميزة في إدارة الذاكرة لأنه مصمم خصيصاً للتطبيقات الموبايل، حيث يقوم بجمع الـ Garbage بشكل أكثر كفاءة من V8. في اختباراتنا، تطبيق React Native مع Hermes استخدم حوالي 30% ذاكرة أقل من نفس التطبيق مع V8 عند تحميل 1000 عنصر في القائمة.
في Flutter، الـ Garbage Collector يعتمد على Dart VM. Dart يستخدم جمع القمامة التوليدي (Generational Garbage Collection)، مما يعني أنه يجمع الـ Garbage بشكل متكرر للأشياء قصيرة العمر، مما يقلل من تأثير الـ GC على أداء التطبيق. لكن المشكلة هنا هي أن Dart VM لا يزال يستخدم ذاكرة أكثر من Hermes في معظم الحالات. في اختبار قمنا به على تطبيق يحتوي على 5000 عنصر في القائمة، استخدم تطبيق Flutter حوالي 120 ميجابايت من الذاكرة مقارنة بـ 80 ميجابايت لتطبيق React Native مع Hermes.
الـ Memory Leak هو كابوس أي مطور موبايل. في React Native، الـ Memory Leak يحدث عادة بسبب عدم إلغاء الاشتراك في الـ Event Listeners أو عدم إغلاق الـ WebSocket Connections بشكل صحيح. المشكلة تكمن في أن الـ Bridge يجعل من الصعب تتبع هذه الـ Leaks، خاصة عندما يكون الـ Leak في الكود الأصلي وليس في جافاسكريبت. في أحد المشاريع التي عملت عليها، اكتشفنا أن تطبيق React Native كان يفقد 10 ميجابايت من الذاكرة كل مرة يفتح فيها المستخدم شاشة معينة بسبب عدم إغلاق اتصال بـ Firebase بشكل صحيح.
في Flutter، الـ Memory Leak يحدث عادة بسبب عدم إغلاق الـ Streams أو الـ Isolates بشكل صحيح. لأن Flutter يستخدم نظاماً واحداً لإدارة الحالة والـ UI، فإن أي خطأ في إدارة الحالة قد يؤدي إلى تسرب ذاكرة يصعب تتبعه. في أحد التطبيقات التي طورناها، اكتشفنا أن استخدام الـ Provider مع عدد كبير من الـ Consumers كان يؤدي إلى تسرب ذاكرة بسبب عدم إلغاء الاشتراك في الـ Consumers عند تغيير الشاشة. الحل كان استخدام مكتبة مثل flutter_bloc التي تدير الـ Subscriptions بشكل أكثر كفاءة.
عندما يتعلق الأمر بالنظام البيئي، لا يوجد منافس لـ React Native. بفضل اعتماد جافاسكريبت، يمكنك العثور على مكتبة لكل شيء تقريباً على npm. هذا يعني أنك تستطيع تطوير تطبيق كامل باستخدام مكتبات جاهزة دون الحاجة لكتابة كود أصلي. لكن هذه الميزة تأتي مع ثمن: جودة المكتبات. في تجربتنا، حوالي 30% من المكتبات المتاحة على npm إما غير مدعومة أو تحتوي على أخطاء تؤثر على أداء التطبيق. المشكلة الأكبر هي أن بعض المكتبات تعتمد على كود أصلي، مما يعني أنك ستضطر لكتابة كود منفصل لكل منصة، وهذا يتعارض مع فلسفة React Native.
Flutter، من ناحية أخرى، لديه نظام بيئي أصغر لكنه أكثر تماسكاً. معظم المكتبات في Flutter مكتوبة بـ Dart وتعمل على كل المنصات دون الحاجة لكود أصلي. هذا يجعل تطوير التطبيقات أكثر سلاسة، لكنك قد تجد نفسك مضطراً لكتابة مكتباتك الخاصة إذا كنت بحاجة لميزة غير مدعومة. في عام 2025، أصبح Flutter يدعم بشكل أفضل تكامل الكود الأصلي عبر الـ Platform Channels، مما يعني أنه يمكنك الآن دمج مكتبات أصلية بسهولة أكبر، لكن هذا يتطلب معرفة بكتابة الكود الأصلي لكل منصة.
في عالم الأعمال، الوقت هو المال. React Native يسمح لك بكتابة كود واحد وتشغيله على iOS وAndroid، مما يقلل من وقت التطوير بشكل كبير. لكن هذا لا يعني أن التكلفة الإجمالية أقل دائماً. في المشاريع الكبيرة، قد تجد نفسك مضطراً لكتابة كود أصلي لبعض الميزات، مما يعني أنك ستحتاج لمطورين خبراء في كل منصة. في أحد المشاريع التي عملنا عليها، تطلب تطبيق React Native فريقاً مكوناً من 3 مطورين جافاسكريبت ومطورين أصليين (iOS وAndroid)، مما زاد من تكلفة المشروع بنسبة 20%.
Flutter، من ناحية أخرى، يسمح لك بتطوير التطبيق باستخدام فريق أصغر، حيث أن معظم الكود مكتوب بـ Dart ويعمل على كل المنصات. لكن هذا لا يعني أن Flutter دائماً الخيار الأرخص. لأن Flutter لا يدعم كل الميزات الأصلية بسهولة، قد تضطر لاستثمار وقت إضافي في كتابة كود أصلي لبعض الميزات المتقدمة. في عام 2025، أصبح Flutter يدعم بشكل أفضل تكامل الكود الأصلي، لكن هذا يتطلب معرفة أعمق بالكود الأصلي، مما قد يزيد من تكلفة التطوير إذا لم يكن فريقك لديه هذه الخبرة.
في عام 2024، قمنا بتطوير تطبيق تجارة إلكترونية لشركتين مختلفتين: إحداهما باستخدام React Native والأخرى باستخدام Flutter. التطبيق الذي استخدم React Native استغرق 6 أشهر للتطوير وكان فريقه مكوناً من 5 مطورين (3 جافاسكريبت و2 أصليين). التطبيق الذي استخدم Flutter استغرق 5 أشهر وكان فريقه مكوناً من 3 مطورين فقط. لكن عند إطلاق التطبيقين، واجهنا مشكلة في تطبيق Flutter: الأداء كان جيداً على الأجهزة القوية، لكن على الأجهزة القديمة كان التطبيق يعاني من الـ Jank عند التمرير السريع في القوائم الطويلة. في المقابل، تطبيق React Native كان يعمل بشكل أكثر سلاسة على الأجهزة القديمة بفضل تحسينات Hermes.
في عام 2025، أصبح من الواضح أن كلا الإطارين قد تطور بشكل كبير، لكن لكل منهما نقاط قوة وضعف مختلفة. React Native أصبح أكثر استقراراً وأسرع بفضل تحسينات Hermes وFabric، لكنه لا يزال يعاني من مشاكل في الـ Latency بسبب الـ Bridge. Flutter أصبح أكثر نضجاً ويدعم تكامل أفضل مع الكود الأصلي، لكنه لا يزال يعاني من مشاكل في إدارة الذاكرة والأداء على الأجهزة القديمة.
في رأيي، المستقبل سيكون لـ Flutter في التطبيقات التي تعتمد على الرسوميات أو تحتاج لأداء عالي، مثل الألعاب أو التطبيقات المالية المعقدة. أما React Native فسيظل الخيار الأفضل للتطبيقات التي تعتمد على النظام البيئي الواسع لجافاسكريبت أو تحتاج لدعم متعدد المنصات بسهولة. لكن الحقيقة هي أن كلا الإطارين قد وصل إلى مستوى من النضج يجعل الاختيار بينهما يعتمد على احتياجات المشروع أكثر من أي شيء آخر. إذا كنت تعمل في شركة ناشئة وتريد إطلاق تطبيق سريعاً، React Native قد يكون الخيار الأفضل. إذا كنت تطور تطبيقاً معقداً يعتمد على الرسوميات، Flutter قد يكون الخيار الأكثر أماناً.
إذا كنت تختار بين React Native وFlutter في 2025، لا تسأل أي إطار أفضل، بل اسأل: ما هي نقطة ضعف التطبيق الذي سأطوره؟ إذا كان التطبيق يعتمد على قوائم طويلة أو بيانات متغيرة باستمرار، اختر React Native مع Hermes وتأكد من تحسين الـ FlatList. إذا كان التطبيق يعتمد على رسوميات معقدة أو animations متقدمة، اختر Flutter واستخدم الـ Isolates لفصل العمليات الثقيلة. وفي كل الأحوال، لا تعتمد على الأرقام النظرية، بل اختبر كلا الإطارين على جهاز حقيقي تحت ظروف مشابهة لظروف المستخدمين الحقيقيين. لأن في النهاية، ما يهم ليس أي إطار أسرع على الورق، بل أيهما ينجو عندما يتحول التطبيق من فكرة إلى واقع تحت ضغط ملايين المستخدمين.