如何在 React Native 实现类微信小程序平台:WebView 调用原生组件
在《我们是如何将 Cordova 应用嵌入到 React Native 中》 一文中,我们简单地介绍了『React Native 重写 Cordova 插件:复杂插件的调用』步骤:
- WebView 调用 RN 方法,并监听 React Native 返回的相应事件
- React Native 接收到 WebView 的调用,调用原生代码,并监听原生代码返回的相应事件
- 原生代码执行 React Native 调用的方法,并响应事件给 React Native
- React Native 接收到原生代码的值,执行 injectJavaScript 注入代码到 WebView 里并执行
- 注入的 JavaScript 执行代码,并发出相应的广播
- WebView 调用的地方,接收到广播,执行相应的方法
上面的 4 和 5 可以是:
4.React Native 接收到原生代码的值,并返回给原生代码 5.接收到相应的值,并发出相应的广播。
即:
本文则详细讨论一下这个过程。
步骤1:WebView 调用 RN 方法,并监听 React Native 返回的相应事件
这里,我们和《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,并传递相应的参数。
步骤2:React Native 接收到 WebView 的调用,调用原生代码,并监听原生代码返回的相应事件
在 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);};
步骤3:原生代码执行 React Native 调用的方法,并响应事件给 React Native
如上,由于 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]; });}
步骤4:React Native 接收到原生代码的值,并返回给原生代码
在这个例子里,由于在 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);
步骤5:接收到相应的值,并发出相应的广播
紧接着,就回到步骤一中的 handler:
function handler(event) { event.target.removeEventListener('message', handler); var data = JSON.parse(event.data); $rootScope.$broadcast('Bridge.datePicker', data); }
步骤6:WebView 调用的地方,接收到广播,执行相应的方法
最后,我们终于到了:
$rootScope.$on('Bridge.datePicker', function(event, data) {// 更新时间});
如此复杂的过程,也是。。。
好了,就差一个小程序框架了:
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Spring 资源文件处理
- Spring profile配置应用
- Spring Bean的加载
- Spring ApplicationContext 简介
- Spring 配置String转Date
- zookeeper事务
- Thread Object wait() notify()基本
- 基本排序算法(冒泡排序 选择排序 插入排序 快速排序 归并排序 基数排序 希尔排序)
- Java加解密AES、DES、TripleDES、MD5、SHA
- Java 根据经纬度计算两点之间的距离
- mysql bin log配置及查看
- mybatis interceptor 处理查询参数及查询结果
- Mybatis基础
- java mix 知识点
- js 加密 crypto-js des加密