在《我们是如何将 Cordova 应用嵌入到 React Native 中》 一文中,我们简单地介绍了『React Native 重写 Cordova 插件:复杂插件的调用』步骤:
上面的 4 和 5 可以是:
4.React Native 接收到原生代码的值,并返回给原生代码 5.接收到相应的值,并发出相应的广播。
即:
本文则详细讨论一下这个过程。
这里,我们和《React Native + Cordova WebView 演进:Plugin 篇》中一样,仍然以 DatePicker 为例。
首先,我们需要一个广播:当 React Native 返回值时,我们就发出一个广播,这样可以解耦合代码。下面的代码则监听相应的广播:
$rootScope.$on('Bridge.datePicker', function(event, data) {// 更新时间});
然后便是相应的 datePicker 的调用:
function datePicker(options) { function handler(event) { event.target.removeEventListener('message', handler); var data = JSON.parse(event.data); $rootScope.$broadcast('Bridge.datePicker', data); } window.document.addEventListener('message', handler); window.postMessage(JSON.stringify({ action: 'DATE_PICKER', payload: payload }));}
先监听从 React Native 发过来的内容,当接收到内容将数据以广播的形式发出。然后,再通过 PostMessage 告诉 React Naitve,我们需要在调用哪个 action,并传递相应的参数。
在 WebView 的 onMessage 方法里,我们需要处理不同的 action:
onMessage = (evt, webView) => { const event = JSON.parse(evt.nativeEvent.data); const action = event.action; const payload = event.payload ... switch (action) { case 'DATE_PICKER': { return DatePickerHandler.showDatePicker(payload, webView); } } ...}
然后根据传过来的 action 类型,调用相应的方法,如这里是 DatePickerHandler.showDatePicker,其 Android 部分代码如下所示:
const { action, year, month, day } = await DatePickerAndroid.open(options); if (action !== DatePickerAndroid.dismissedAction) { webView.postMessage(JSON.stringify({ type: 'DATE_PICKER', success: true, date, })); }
iOS 则有一些不同,iOS 没有非标签的组件,需要自己写。而且,由于 iOS 的 DatePicker 是异步的,因此我们需要通过事件的方式进行。如下是写完插件后的调用示例:
const RNNoTagDatepicker = NativeModules.RNNoTagDatepicker;const DatePickerEvent = new NativeEventEmitter(NativeModules.RNNoTagDatepicker);...const showPicker = async (options) => { RNNoTagDatepicker.show(options);};
如上,由于 iOS 的日期插件是异步的,并且它只能通过方法,而非组件的方式来唤醒 UI,故而需要 sendEventWithName 来返回值
RCT_EXPORT_METHOD(show:(NSDictionary *) options) { dispatch_async(dispatch_get_main_queue(), ^{ }}#pragma mark - Actions- (IBAction)doneAction:(id)sender { dispatch_async(dispatch_get_main_queue(), ^{ NSTimeInterval seconds = [self.datePicker.date timeIntervalSince1970]; [self sendEventWithName:@"DATEPICKER_NATIVE_INVOKE" body: @{@"status": @"success", @"value": [NSString stringWithFormat:@"%f", seconds]}]; [self hide]; });}
在这个例子里,由于在 WebView 以广播的方式解绑,因此可以直接返回值:
DatePickerEvent.addListener('DATEPICKER_NATIVE_INVOKE', (evt: Event) => { ... webView.postMessage(JSON.stringify({ type: 'DATE_PICKER', success: true, date })); ...});
如果是要不断地发送数据,则需要在 RN 代码里执行:
let js = 'var event = new CustomEvent("' + action + '", {detail: ' + JSON.stringify(detail) + '});';js += 'window.document.dispatchEvent(event);';webView.injectJavaScript(js);
紧接着,就回到步骤一中的 handler:
function handler(event) { event.target.removeEventListener('message', handler); var data = JSON.parse(event.data); $rootScope.$broadcast('Bridge.datePicker', data); }
最后,我们终于到了:
$rootScope.$on('Bridge.datePicker', function(event, data) {// 更新时间});
如此复杂的过程,也是。。。
好了,就差一个小程序框架了: