最近,项目上正在打算使用 React Native 来重写/重构/演讲原来的应用。由于早先使用 Cordova + Ionic 的时候,项目的业务代码很长一段时间里,主要是由我一个编写的。与此同时,也不会分配充足的人力,用于重写现有的业务逻辑。
因此,作为一个咨询师,我提供了几个不同的重构方案,并建议客户使用 React Native + WebView 的形式来进行演进。即在 React Native 里使用 WebView 来嵌入原有的业务逻辑,新的业务逻辑则采用 React Native 进行。考虑到未来的一段时间内, 业务代码将会继续采用 WebView 编写,而技术代码则用 React Native 编写,这是比较理想的方案。
而作为演进的其中一个难点是:重写原有的 Cordova 插件。我们使用到了数量众多的 Cordova 插件,如 Toast、日期控件等等。这个时候,就需要借助于 React Native WebView 的通信来做这件事。
早期的 React Native WebView 并不能直接与 WebView 通信,而自 React Native 0.37 版本后,则提供了:
两个方法来与 WebView 进行交互,如下是一个简单的 DEMO:
...
class xxWebView extends React.Component {
webview: WebView
handleMessage = (evt: any) => {
// doSomething()
}
render() {
return (
<WebView
ref={webview => this.webview = webview}
onMessage={this.handleMessage}
/>
)
}
}
在我们的 WebView 里,只需要执行下:
window.postMessage({ plugin: 'TOAST' })
就可以向插件发送信息。因此,对于完成我们的插件来说,只需要做到下面的步骤:
接着,让我们来看一个简单的日期控件的 DEMO。
重写这段逻辑前,先让我们来看看原有的逻辑代码:
function onSuccess(date) {
// 更新时间
}
datePicker.show(options, onSuccess, null);
我们通过 options 来传递参数,而 onSuccess 则是成功的回调。不过,由于已经没有 Cordova 的机制,这里的 success 和 error 的回调就没有啥用了。
因此,在 WebView 上这段逻辑就变成了:
$rootScope.$on('Bridge.datePicker', function(event, data) {
// 更新时间
});
BridgeHelper.datePicker(options);
//BridgeHelper.js 中的相关代码
window.postMessage(JSON.stringify({
command: 'DATE_PICKER',
payload: options
}));
同时,我们有一个全局的监听函数,在这里面判断是否有对应的 command 类型。如果是我们需要的 DATE_PICKER,并且是成功地修改值,便会发出这样的一个广播,上面的代码就可以成功地更新时间。
window.document.addEventListener('message', function (e) {
var data = JSON.parse(e.data);
if(data.command && data.command === 'DATE_PICKER' && data.success) {
$rootScope.$broadcast('Bridge.datePicker', data)
}
});
这个原理与之前提到的 Ionic 与 Cordova 插件编写:基于事件与广播的机制 是相似的,通过全局事件来控制逻辑。
在 React Native 端,则也是对相应的 handleMessage 进行处理,然后调用相应的组件来处理,如下是调用系统的控件:
DatePickerHandler.showDatePicker = (payload, webView) => {
const showPicker = async (options, webView) => {
try {
const { command, year, month, day } = await DatePickerAndroid.open(options);
if (command === DatePickerAndroid.dismissedcommand) {
//
} else {
const date = new Date(year, month, day);
webView.postMessage(JSON.stringify({
command: 'DATE_PICKER',
success: true,
date,
}));
}
} catch ({ code, message }) {
console.warn('Cannot open date picker', message);
}
};
showPicker(options, webView);
};
通过这样复杂的工作,我们就可以完成大部分的工作。
围观我的Github Idea墙, 也许,你会遇到心仪的项目