React Native + Cordova WebView 演進:Plugin 篇
最近,項目上正在打算使用 React Native 來重寫/重構/演講原來的應用。由于早先使用 Cordova + Ionic 的時候,項目的業務代碼很長一段時間里,主要是由我一個編寫的。與此同時,也不會分配充足的人力,用于重寫現有的業務邏輯。
因此,作為一個咨詢師,我提供了幾個不同的重構方案,并建議客戶使用 React Native + WebView 的形式來進行演進。即在 React Native 里使用 WebView 來嵌入原有的業務邏輯,新的業務邏輯則采用 React Native 進行。考慮到未來的一段時間內, 業務代碼將會繼續采用 WebView 編寫,而技術代碼則用 React Native 編寫,這是比較理想的方案。
而作為演進的其中一個難點是:重寫原有的 Cordova 插件。我們使用到了數量眾多的 Cordova 插件,如 Toast、日期控件等等。這個時候,就需要借助于 React Native WebView 的通信來做這件事。
React Native WebView 通信
早期的 React Native WebView 并不能直接與 WebView 通信,而自 React Native 0.37 版本后,則提供了:
- onMessage
- postMessage
兩個方法來與 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' })
就可以向插件發送信息。因此,對于完成我們的插件來說,只需要做到下面的步驟:
- 當需要調用原生插件的時候,在 WebView 里調用 window.postMessage 來傳遞,相應的 插件名 + 插件的參數
- React Native 通過 onMessage 來處理對應的類型,并調用對應的插件
- 當需要返回結果時,通過 webview.postMessage 來傳遞參數,并帶上相應的 插件名 + 返回結果
- 在 WebView 端 ,如果想獲取返回的結果,則需要 window.document.addEventListener 來監聽 message 事件
- 最后,再根據返回的值來做相應的處理。
接著,讓我們來看一個簡單的日期控件的 DEMO。
Cordova WebView 調用 React Native 日期控件
WebView
重寫這段邏輯前,先讓我們來看看原有的邏輯代碼:
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
在 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);
};
通過這樣復雜的工作,我們就可以完成大部分的工作。
來自:http://www.phodal.com/blog/react-native-inside-cordova-webview-with-plugin/