简介
如果你看的这一篇,需要你对ReactNative的开发有一定的了解,此文讲的是在ReactNative提供的组件不能满足需求,或者native用于较成熟的组件想要输出,那么就需要用到自定义组件了.
通过该文,我们也可以对native和JS交互方式进行初步了解,关于输出方法内部实现,我们下一篇再剖.
Native module
native module就是实现了RCTBridgeModule
协议的OC类.RCT就是ReaCT的缩写.
具体步骤如下
- 引入
#import <React/RCTBridgeModule.h>
类,然后遵守RCTBridgeModule
协议. - 实现
RCT_EXPORT_MODULE(customName)
方法. customName是自定义的组件名,如果不填默认为当前类名.这个组件名是用于向JS输出.
输出组建后,默认不会向JS输出任何方法,想要输出方法,需要自定义实现方法输出,使用宏RCT_EXPORT_METHOD ()
RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location)
{
RCTLogInfo(@"Pretending to create an event %@ at %@", name, location);
}
对于JS端,调用时就可以如下
import {NativeModules} from 'react-native';
var CustomName = NativeModules.CalendarManager;
CustomName.addEvent('Birthday Party', '4 Privet Drive, Surrey');
注意
向JS输出的方法名,是RCT_EXPORT_METHOD之后,第一个冒号之前的名字.如果native已经暴露了多个冒号之前同名的方法,RN提供了
RCT_REMAP_METHOD ()
来制定方法名.
另外一点, RCT_EXPORT_METHOD回调进入的方法,默认并不在主线程,如果想要进行主线程的方法调用,需要手动进行
dispatch_async(dispatch_get_main_queue(), ^{});
回到主线程
RCT_EXPORT_METHOD
参数
RCT_EXPORT_METHOD
支持如下的参数类型
- string (NSString)
- number (NSInteger, float, double, CGFloat, NSNumber)
- boolean (BOOL, NSNumber)
- array (NSArray) of any types from this list
- object (NSDictionary) with string keys and values of any type from this list
- function (RCTResponseSenderBlock)
也支持所有RCTConvert
支持的类型.
回调
native module支持回调类型RCTResponseSenderBlock
RCT_EXPORT_METHOD(findEvents:(RCTResponseSenderBlock)callback)
{
NSArray *events = ...
callback(@[[NSNull null], events]);
}
RCTResponseSenderBlock
只支持一个参数:一个包含了多个参数的数组.在JS端可以如下,默认第一个参数是error.当没有错误时error为空.
CalendarManager.findEvents((error, events) => {
if (error) {
console.error(error);
} else {
this.setState({events: events});
}
});
native module只能调用一次回调.如果想传递错误,通过RCTUtils.h
类中的RCTMakeError
来创建.
Promise
Promise
是用于实现异步操作async/await
的工具类.如果最后一个参数类型为RCTPromiseResolveBlock
和RCTPromiseRejectBlock
,JS端会返回一个promise对象,进行一步操作.
RCT_REMAP_METHOD(findEvents,
findEventsWithResolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
NSArray *events = ...
if (events) {
resolve(events);
} else {
NSError *error = ...
reject(@"no_events", @"There were no events", error);
}
}
JS端因为获取的是promise对象,可以使用await
关键字进行异步调用并等待结果
async function updateEvents() {
try {
var events = await CalendarManager.findEvents();
this.setState({events});
} catch (e) {
console.error(e);
}
}
updateEvents();
关于线程
JS执行native module是在一个单独的线程实现的,可以通过- (dispatch_queue_t)methodQueue
来控制.如果返回主线程,所有执行的方法会在主线程被执行.
- (dispatch_queue_t)methodQueue
{
return dispatch_get_main_queue();
}
方法methodQueue
之后在组件初始化时被调用一次.
输出实例
除了可以输出方法,还有输出实例.
- (NSDictionary *)constantsToExport
{
return @{ @"firstDayOfTheWeek": @"Monday" };
}
在JS端可以直接获取console.log(CalendarManager.firstDayOfTheWeek);
只有在初始化时实例输出才是有效的,如果在运行时修改constantsToExport是不会影响JS环境的数据的.
输出枚举
通过typedef NS_ENUM()
定义的枚举,可以通过增加RCTConvert
的扩展来完成
@implementation RCTConvert (StatusBarAnimation)
RCT_ENUM_CONVERTER(UIStatusBarAnimation, (@{ @"statusBarAnimationNone" : @(UIStatusBarAnimationNone),
@"statusBarAnimationFade" : @(UIStatusBarAnimationFade),
@"statusBarAnimationSlide" : @(UIStatusBarAnimationSlide)}),
UIStatusBarAnimationNone, integerValue)
@end
之后就可以通过输出属性和方法等方式在JS中使用了.
native向JS发方法
想给JS发方法,可以继承类RCTEventEmitter
,实现supportedEvents
方法,然后通过调用self sendEventWithName:
即可.
RCT_EXPORT_MODULE();
- (NSArray<NSString *> *)supportedEvents
{
return @[@"EventReminder"];
}
- (void)calendarEventReminderReceived:(NSNotification *)notification
{
NSString *eventName = notification.userInfo[@"name"];
[self sendEventWithName:@"EventReminder" body:@{@"name": eventName}];
}
JS端可以通过NativeEventEmitter
进行注册和调用
import { NativeEventEmitter, NativeModules } from 'react-native';
const { CalendarManager } = NativeModules;
const calendarManagerEmitter = new NativeEventEmitter(CalendarManager);
const subscription = calendarManagerEmitter.addListener(
'EventReminder',
(reminder) => console.log(reminder.name)
);
...
// Don't forget to unsubscribe, typically in componentWillUnmount
subscription.remove();
注意取消订阅,一般在componentWillUnmount
内执行.
客户端可以通过一些方式获取JS注册和移除订阅的事件,来优化只在有订阅者的情况下才发送事件.
// Will be called when this module's first listener is added.
-(void)startObserving {
hasListeners = YES;
// Set up any upstream listeners or background tasks as necessary
}
// Will be called when this module's last listener is removed, or on dealloc.
-(void)stopObserving {
hasListeners = NO;
// Remove upstream listeners, stop unnecessary background tasks
}