الوحدة النمطية الأصلية في React Native — ما هي، وكيفية إنشائها ودمجها

المؤلف: IT Sectr نُشر: 2026-06-03 وقت القراءة: 9 دق

الوحدة النمطية الأصلية هي فئة Java أو Objective-C تجعل واجهات برمجة التطبيقات الأصلية للمنصة متاحة من JavaScript في React Native. يتم تسجيل كل وحدة في Bridge وتصدر طرقًا يمكن استدعاؤها من كود JS كوظائف عادية. وفقًا لـ Meta، 2024، تظل الوحدة النمطية الأصلية الطريقة الرئيسية لدمج كود المنصة في تطبيقات React Native.

النقاط الرئيسية

  • الوحدة النمطية الأصلية — جسر بين JavaScript وكود iOS أو Android الأصلي.
  • التسجيل — يتم تعريف الوحدة من خلال تعليقات iOS (RCT_EXPORT_MODULE) أو Android (@ReactMethod).
  • أنواع البيانات — يتم دعم الأنواع البدائية والسلاسل والمصفوفات والقواميس وPromise.
  • الاستخدام — من JS يمكن الوصول إلى الوحدة عبر NativeModules.اسمالوحدة.
  • الهندسة الجديدة — في React Native 0.76+ يمكن للوحدة النمطية الأصلية العمل كوحدة Turbo عبر JSI.

ما هي الوحدة النمطية الأصلية؟

الوحدة النمطية الأصلية هي عنصر معماري في React Native يسمح بتنفيذ كود بلغة المنصة (Objective-C/Swift لنظام iOS، Java/Kotlin لنظام Android) وإرجاع النتيجة إلى JavaScript. بدون الوحدة النمطية الأصلية، من المستحيل الوصول إلى إمكانيات الجهاز الأصلية — الكاميرا، GPS، مقياس التسارع، نظام الملفات أو Bluetooth.

تأتي React Native مع مجموعة من الوحدات النمطية الأصلية المدمجة: CameraRoll وAsyncStorage وGeolocation وNetInfo وغيرها. ومع ذلك، للمهام المحددة — دمج SDK لجهات خارجية، العمل مع أجهزة الاستشعار أو العمليات الخلفية — يقوم المطور بإنشاء وحدات مخصصة. وفقًا لاستطلاع State of React Native 2024، يستخدم 67% من المطورين وحدة نمطية أصلية مخصصة واحدة على الأقل في مشاريعهم.

تعتمد هندسة الوحدة النمطية الأصلية على إصدار React Native. في الهندسة الكلاسيكية (React Native 0.72 والإصدارات الأقدم) تتصل الوحدة عبر Bridge وتتواصل مع JS بشكل غير متزامن عبر تسلسل JSON. في الهندسة الجديدة (React Native 0.76+) يمكن للوحدة العمل كـ وحدة فائقة، باستخدام JSI للوصول المتزامن دون تسلسل.

إنشاء وحدة نمطية أصلية لنظام iOS

يبدأ إنشاء وحدة نمطية أصلية لنظام iOS بتعريف فئة Objective-C تنفذ بروتوكول RCTBridgeModule. يقوم ماكرو RCT_EXPORT_MODULE بتسجيل الوحدة في Bridge، بينما يقوم RCT_EXPORT_METHOD بتصدير طريقة متاحة من JavaScript.

objective-c
// ImageCompressor.m — الوحدة النمطية الأصلية لنظام iOS
@interface ImageCompressor () RCT_EXPORT_MODULE()
@end

@implementation ImageCompressor

RCT_EXPORT_METHOD(compressImage:(NSString *)imagePath
                  quality:(NSNumber *)quality
                  resolver:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject)
{
    UIImage *image = [UIImage imageWithContentsOfFile:imagePath];
    NSData *compressedData = [UIImageJPEGRepresentation(image, quality.floatValue)];
    NSString *outputPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"compressed.jpg"];
    [compressedData writeToFile:outputPath atomically:YES];
    resolve(outputPath);
}

@end

تستقبل طريقة compressImage مسار الصورة وجودة الضغط (0.0–1.0)، وتعالج البيانات على الجانب الأصلي وتعيد المسار إلى الملف المضغوط. الميزة الرئيسية هي أن الضغط يتم بواسطة كود أصلي، وهو أسرع وأكثر كفاءة في الذاكرة بشكل ملحوظ من العملية المكافئة في JavaScript.

لوحدات Swift، يتم استخدام التعليق @objc قبل الفئة والطرق لجعلها متاحة لوقت تشغيل Objective-C الذي يعمل به Bridge. يجب أن ترث الفئة من NSObject وتنفذ RCTBridgeModule.

swift
// ImageCompressor.swift — وحدة Swift الأصلية
@objc(ImageCompressor)
class ImageCompressor: NSObject {

    @objc
    func compressImage(
        _ imagePath: String,
        quality: Float,
        resolver: @escaping RCTPromiseResolveBlock,
        rejecter: @escaping RCTPromiseRejectBlock
    ) {
        guard let image = UIImage(contentsOfFile: imagePath) else {
            rejecter("FILE_ERROR", "Cannot load image", nil)
            return
        }
        guard let data = image.jpegData(compressionQuality: CGFloat(quality)) else {
            rejecter("COMPRESS_ERROR", "Compression failed", nil)
            return
        }
        let outputPath = NSTemporaryDirectory() + "compressed.jpg"
        try? data.write(to: URL(fileURLWithPath: outputPath))
        resolver(outputPath)
    }
}

إنشاء وحدة نمطية أصلية لنظام Android

في Android، يتم إنشاء وحدة نمطية أصلية كفئة Java ترث من ReactContextBaseJavaModule. يقوم التعليق @ReactMethod بتصدير الطريقة إلى Bridge. يتم استخدام واجهة Promise من com.facebook.react.bridge لإرجاع النتائج.

java
// ImageCompressorModule.java — الوحدة النمطية الأصلية لنظام Android
public class ImageCompressorModule
    extends ReactContextBaseJavaModule {

    @Override
    public String getName() {
        return "ImageCompressor";
    }

    @ReactMethod
    public void compressImage(
            String imagePath,
            Float quality,
            Promise promise) {
        try {
            Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
            File outputFile = new File(
                ReactNative.getApplicationContext()
                    .getCacheDir(), "compressed.jpg");
            FileOutputStream fos = new FileOutputStream(outputFile);
            bitmap.compress(
                Bitmap.CompressFormat.JPEG,
                (int)(quality * 100), fos);
            fos.close();
            promise.resolve(outputFile.getAbsolutePath());
        } catch (Exception e) {
            promise.reject("COMPRESS_ERROR", e.getMessage());
        }
    }
}

تقوم طريقة getName() بإرجاع اسم الوحدة الذي ستكون متاحة به من JavaScript. في المثال أعلاه، تم تسجيل الوحدة باسم ImageCompressor. يشير التعليق @ReactMethod إلى Bridge أنه يجب تصدير الطريقة. مهم: يجب أن تكون الطرق من نوع void وأن تقبل فقط الأنواع المدعومة من Bridge: String وBoolean وInteger وDouble وReadableArray وReadableMap وPromise.

بعد إنشاء فئة الوحدة، يجب تسجيلها في حزمة التطبيق. لهذا، يتم إنشاء فئة تنفذ ReactPackage وإضافتها إلى قائمة الوحدات في طريقة createNativeModules.

java
// ImageCompressorPackage.java — تسجيل الوحدة
public class ImageCompressorPackage implements ReactPackage {

    @Override
    public List<NativeModule> createNativeModules(
            ReactApplicationContext reactContext) {
        return Arrays.asList(
            new ImageCompressorModule(reactContext)
        );
    }

    @Override
    public List<ViewManager> createViewManagers(
            ReactApplicationContext reactContext) {
        return Collections.emptyList();
    }
}

تسجيل واستخدام الوحدة النمطية الأصلية

بعد إنشاء الوحدات لكلا المنصتين، يجب تسجيلها في React Native. لنظام Android، يتم إضافة الحزمة في MainApplication.java في طريقة getPackages(). لنظام iOS، يتم تسجيل الوحدة تلقائيًا عبر ماكرو RCT_EXPORT_MODULE، ولكن يمكن أيضًا استخدام التسجيل اليدوي في AppDelegate.mm.

java
// MainApplication.java — إضافة الحزمة إلى React Native
import com.yourapp.nativemodules.ImageCompressorPackage;

public class MainApplication extends Application
    implements ReactApplication {

    private final ReactNativeHost mReactNativeHost =
        new ReactNativeHost(this) {

        @Override
        protected List<ReactPackage> getPackages() {
            List<ReactPackage> packages =
                new PackageList(this).getPackages();
            packages.add(new ImageCompressorPackage());
            return packages;
        }
    };
}

بعد التسجيل، تصبح الوحدة متاحة في JavaScript عبر NativeModules. تستخدم React Native تلقائيًا اسم الوحدة المحدد في getName() لنظام Android أو RCT_EXPORT_MODULE لنظام iOS.

js
// استخدام الوحدة النمطية الأصلية من JavaScript
import { NativeModules } from 'react-native';
import { Platform } from 'react-native';

const ImageCompressor = NativeModules.ImageCompressor;

async function compressPhoto(uri: string) {
  try {
    const result = await ImageCompressor.compressImage(
      uri.replace('file://', ''), 0.8
    );
    console.log('تم الضغط:', result);
    return result;
  } catch (error) {
    console.error('فشل الضغط:', error);
    throw error;
  }
}

الوحدة النمطية الأصلية مقابل الوحدة الفائقة: مقارنة

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

الخاصيةالوحدة النمطية الأصلية (Bridge)الوحدة الفائقة (JSI)
الاتصالغير متزامن عبر JSONمتزامن عبر JSI
التسلسلJSON في كل استدعاءبدون نسخ بيانات
الكتابةيدوي، بدون توليدتلقائي عبر Codegen
التحميلعند تهيئة التطبيقتحميل بطيء (lazy load)
التوافقجميع إصدارات React NativeReact Native 0.73+

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

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

هل يمكن تمرير callback بدلاً من Promise في الوحدة النمطية الأصلية؟

نعم، يدعم Bridge الـ callbacks. بدلاً من Promise، يمكن استخدام دوال callback RCTResponseSenderBlock في iOS وCallback في Android. ومع ذلك، يعتبر Promise المعيار الحديث ويُوصى به للوحدات الجديدة.

كيفية تصحيح أخطاء الوحدة النمطية الأصلية في Xcode أو Android Studio؟

يتم تصحيح أخطاء الوحدة النمطية الأصلية ككود أصلي عادي — ضع نقاط توقف في Xcode أو Android Studio. لنظام iOS، استخدم مخطط البناء مع React Native؛ لنظام Android، استخدم تهيئة Debug. نقطة الدخول هي الطرق التي يتم استدعاؤها من JS.

ما أنواع البيانات التي لا تدعمها الوحدة النمطية الأصلية؟

الوحدة النمطية الأصلية عبر Bridge لا تدعم البيانات الثنائية (NSData/byte[]) أو الكائنات المخصصة أو الدوال. للصور، استخدم مسار الملف أو سلسلة base64. الوحدة الفائقة عبر JSI تزيل بعض هذه القيود.

هل أحتاج إلى إعادة كتابة الوحدة النمطية الأصلية عند تحديث React Native؟

عادة لا — واجهة برمجة الوحدة النمطية الأصلية مستقرة ومتوافقة مع الإصدارات السابقة. عند الانتقال إلى الهندسة الجديدة (الوحدة الفائقة)، يتم تكييف الوحدة من خلال الديكورات، لكن الكود الموجود يستمر في العمل.

كيفية إرسال حدث من الوحدة النمطية الأصلية إلى JavaScript؟

استخدم RCTEventEmitter في iOS أو DeviceEventEmitter في Android. ترسل الوحدة حدثًا، ويشترك جانب JS عبر NativeEventEmitter من react-native. هذا مفيد لبيانات التدفق وأحداث الاستشعار.

الملخص

  • الوحدة النمطية الأصلية — فئة Java/Objective-C تجعل واجهات برمجة التطبيقات الأصلية متاحة من JavaScript في React Native.
  • التسجيل — iOS يستخدم RCT_EXPORT_MODULE، Android يستخدم ReactContextBaseJavaModule مع @ReactMethod.
  • الاستدعاء — من JS يتم الوصول إلى الوحدة عبر NativeModules.Module وتدعم Promise وcallback.
  • أنواع البيانات — Bridge ينقل فقط الأنواع المتوافقة مع JSON: السلاسل والأرقام والمصفوفات والقواميس.
  • الأداء — الوحدة النمطية الأصلية الكلاسيكية أقل من الوحدة الفائقة بسبب عدم التزامن والتسلسل.
  • الترحيل — عند الترقية إلى React Native 0.76+ يمكن للوحدة العمل كوحدة فائقة دون إعادة كتابة المنطق.
  • التطبيق — الوحدة النمطية الأصلية لا غنى عنها للكاميرا وBluetooth ونظام الملفات ودمج SDK.

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا