Native Module คือคลาส Java หรือ Objective-C ที่ทำให้ API เนทีฟของแพลตฟอร์มสามารถเข้าถึงได้จาก JavaScript ใน React Native แต่ละโมดูลจะลงทะเบียนใน Bridge และส่งออกเมธอดที่สามารถเรียกจากโค้ด JS ได้เหมือนฟังก์ชันทั่วไป ตามข้อมูลจาก Meta, 2024 Native Module ยังคงเป็นวิธีหลักในการรวมโค้ดแพลตฟอร์มเข้ากับแอปพลิเคชัน React Native
ประเด็นสำคัญ
Native Module คือองค์ประกอบทางสถาปัตยกรรมของ React Native ที่อนุญาตให้รันโค้ดในภาษาแพลตฟอร์ม (Objective-C/Swift สำหรับ iOS, Java/Kotlin สำหรับ Android) และส่งคืนผลลัพธ์ไปยัง JavaScript หากไม่มี Native Module จะไม่สามารถเข้าถึงความสามารถเนทีฟของอุปกรณ์ได้ — กล้อง GPS เซ็นเซอร์วัดความเร่ง ระบบไฟล์ หรือ Bluetooth
React Native มาพร้อมกับชุด Native Module ในตัว: CameraRoll, AsyncStorage, Geolocation, NetInfo และอื่น ๆ อย่างไรก็ตาม สำหรับงานเฉพาะ — การรวม SDK ของบุคคลที่สาม การทำงานกับเซ็นเซอร์ฮาร์ดแวร์ หรือกระบวนการพื้นหลัง — นักพัฒนาจะสร้างโมดูลที่กำหนดเอง ตามการสำรวจ State of React Native 2024 นักพัฒนา 67% ใช้ Native Module ที่กำหนดเองอย่างน้อยหนึ่งตัวในโปรเจกต์ของพวกเขา
สถาปัตยกรรม Native Module ขึ้นอยู่กับเวอร์ชันของ React Native ในสถาปัตยกรรมแบบคลาสสิก (React Native 0.72 และเก่ากว่า) โมดูลจะเชื่อมต่อผ่าน Bridge และสื่อสารกับ JS แบบอะซิงโครนัสผ่านการทำให้เป็นอนุกรม JSON ในสถาปัตยกรรมใหม่ (React Native 0.76+) โมดูลสามารถทำงานเป็น Turbo Module โดยใช้ JSI สำหรับการเข้าถึงแบบซิงโครนัสโดยไม่ต้องทำให้เป็นอนุกรม
การสร้าง Native Module สำหรับ iOS เริ่มต้นด้วยการประกาศคลาส Objective-C ที่ใช้โปรโตคอล RCTBridgeModule มาโคร RCT_EXPORT_MODULE ลงทะเบียนโมดูลใน Bridge และ RCT_EXPORT_METHOD ส่งออกเมธอดที่พร้อมใช้งานจาก JavaScript
// ImageCompressor.m — Native Module สำหรับ 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
// 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 Native Module ถูกสร้างเป็นคลาส Java ที่ขยาย ReactContextBaseJavaModule คำอธิบายประกอบ @ReactMethod ส่งออกเมธอดไปยัง Bridge อินเทอร์เฟซ Promise จาก com.facebook.react.bridge ใช้สำหรับส่งคืนผลลัพธ์
// ImageCompressorModule.java — Native Module สำหรับ 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
// 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 ได้เช่นกัน
// 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 โดยอัตโนมัติ
// การใช้ Native Module จาก 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;
}
}
การเปรียบเทียบ Native Module แบบคลาสสิกกับ Turbo Module ช่วยให้เข้าใจว่าควรเลือกแนวทางใดสำหรับโปรเจกต์ใหม่ ทั้งสองกลไกให้การเข้าถึงโค้ดเนทีฟ แต่แตกต่างกันโดยพื้นฐานในด้านสถาปัตยกรรมและประสิทธิภาพ
| คุณลักษณะ | Native Module (Bridge) | Turbo Module (JSI) |
|---|---|---|
| การสื่อสาร | อะซิงโครนัสผ่าน JSON | ซิงโครนัสผ่าน JSI |
| การทำให้เป็นอนุกรม | JSON ทุกการเรียก | ไม่ต้องคัดลอกข้อมูล |
| การกำหนดประเภท | ด้วยตนเอง ไม่มีการสร้าง | อัตโนมัติผ่าน Codegen |
| การโหลด | เมื่อเริ่มต้นแอปพลิเคชัน | โหลดแบบขี้เกียจ (lazy load) |
| ความเข้ากันได้ | React Native ทุกเวอร์ชัน | React Native 0.73+ |
สำหรับโปรเจกต์ที่มีอยู่บน React Native 0.72 และเก่ากว่า Native Module แบบคลาสสิกยังคงเป็นตัวเลือกหลัก สำหรับโปรเจกต์ใหม่ แนะนำให้ใช้ Turbo Module โดยเฉพาะอย่างยิ่งหากต้องการประสิทธิภาพสูงในการเรียกเมธอดเนทีฟบ่อยครั้ง ด้วยการอัปเดต React Native อย่างค่อยเป็นค่อยไป ชุมชนกำลังก้าวไปสู่การเปลี่ยนผ่านอย่างสมบูรณ์สู่สถาปัตยกรรมใหม่
คำถามที่พบบ่อย
ได้ Bridge รองรับ callback แทนที่จะใช้ Promise สามารถใช้ฟังก์ชัน callback RCTResponseSenderBlock ใน iOS และ Callback ใน Android อย่างไรก็ตาม Promise ถือเป็นมาตรฐานสมัยใหม่และแนะนำสำหรับโมดูลใหม่
Native Module ถูกดีบักเหมือนโค้ดเนทีฟทั่วไป — ตั้งเบรกพอยต์ใน Xcode หรือ Android Studio สำหรับ iOS ให้ใช้สคีมาบิวด์กับ React Native สำหรับ Android ให้ใช้การกำหนดค่า Debug จุดเริ่มต้นคือเมธอดที่เรียกจาก JS
Native Module ผ่าน Bridge ไม่รองรับข้อมูลไบนารี (NSData/byte[]) ออบเจ็กต์ที่กำหนดเอง และฟังก์ชัน สำหรับรูปภาพ ให้ใช้พาธไฟล์หรือสตริง base64 Turbo Module ผ่าน JSI จะลบข้อจำกัดเหล่านี้บางส่วน
โดยทั่วไปไม่ — API ของ Native Module มีความเสถียรและเข้ากันได้ย้อนหลัง เมื่อเปลี่ยนไปใช้สถาปัตยกรรมใหม่ (Turbo Module) โมดูลจะถูกปรับผ่านเดคอเรเตอร์ แต่โค้ดที่มีอยู่ยังคงทำงานต่อไป
ใช้ RCTEventEmitter ใน iOS หรือ DeviceEventEmitter ใน Android โมดูลส่งอีเวนต์ และฝั่ง JS สมัครสมาชิกผ่าน NativeEventEmitter จาก react-native ซึ่งมีประโยชน์สำหรับข้อมูลสตรีมมิ่งและอีเวนต์เซ็นเซอร์
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม