React Native + Cordova WebView 演进:Plugin 篇
JulGavin
7年前
<p>最近,项目上正在打算使用 React Native 来重写/重构/演讲原来的应用。由于早先使用 Cordova + Ionic 的时候,项目的业务代码很长一段时间里,主要是由我一个编写的。与此同时,也不会分配充足的人力,用于重写现有的业务逻辑。</p> <p>因此,作为一个咨询师,我提供了几个不同的重构方案,并建议客户使用 React Native + WebView 的形式来进行演进。即在 React Native 里使用 WebView 来嵌入原有的业务逻辑,新的业务逻辑则采用 React Native 进行。考虑到未来的一段时间内, 业务代码将会继续采用 WebView 编写,而技术代码则用 React Native 编写,这是比较理想的方案。</p> <p>而作为演进的其中一个难点是:重写原有的 Cordova 插件。我们使用到了数量众多的 Cordova 插件,如 Toast、日期控件等等。这个时候,就需要借助于 React Native WebView 的通信来做这件事。</p> <h2>React Native WebView 通信</h2> <p>早期的 React Native WebView 并不能直接与 WebView 通信,而自 React Native 0.37 版本后,则提供了:</p> <ul> <li>onMessage</li> <li>postMessage</li> </ul> <p>两个方法来与 WebView 进行交互,如下是一个简单的 DEMO:</p> <pre> <code class="language-javascript">... class xxWebView extends React.Component { webview: WebView handleMessage = (evt: any) => { // doSomething() } render() { return ( <WebView ref={webview => this.webview = webview} onMessage={this.handleMessage} /> ) } }</code></pre> <p>在我们的 WebView 里,只需要执行下:</p> <pre> <code class="language-java">window.postMessage({ plugin: 'TOAST' })</code></pre> <p>就可以向插件发送信息。因此,对于完成我们的插件来说,只需要做到下面的步骤:</p> <ul> <li>当需要调用原生插件的时候,在 WebView 里调用 window.postMessage 来传递,相应的 <strong>插件名 + 插件的参数</strong></li> <li>React Native 通过 onMessage 来处理对应的类型,并调用对应的插件</li> <li>当需要返回结果时,通过 webview.postMessage 来传递参数,并带上相应的 <strong>插件名 + 返回结果</strong></li> <li>在 WebView 端 ,如果想获取返回的结果,则需要 window.document.addEventListener 来监听 message 事件</li> <li>最后,再根据返回的值来做相应的处理。</li> </ul> <p>接着,让我们来看一个简单的日期控件的 DEMO。</p> <h2>Cordova WebView 调用 React Native 日期控件</h2> <h3>WebView</h3> <p>重写这段逻辑前,先让我们来看看原有的逻辑代码:</p> <pre> <code class="language-java">function onSuccess(date) { // 更新时间 } datePicker.show(options, onSuccess, null);</code></pre> <p>我们通过 options 来传递参数,而 onSuccess 则是成功的回调。不过,由于已经没有 Cordova 的机制,这里的 success 和 error 的回调就没有啥用了。</p> <p>因此,在 WebView 上这段逻辑就变成了:</p> <pre> <code class="language-java">$rootScope.$on('Bridge.datePicker', function(event, data) { // 更新时间 }); BridgeHelper.datePicker(options); //BridgeHelper.js 中的相关代码 window.postMessage(JSON.stringify({ command: 'DATE_PICKER', payload: options }));</code></pre> <p>同时,我们有一个全局的监听函数,在这里面判断是否有对应的 command 类型。如果是我们需要的 DATE_PICKER,并且是成功地修改值,便会发出这样的一个广播,上面的代码就可以成功地更新时间。</p> <pre> <code class="language-java">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) } });</code></pre> <p>这个原理与之前提到的 <a href="/misc/goto?guid=4959749240335412648" rel="nofollow,noindex">Ionic 与 Cordova 插件编写:基于事件与广播的机制</a> 是相似的,通过全局事件来控制逻辑。</p> <h3>React Native</h3> <p>在 React Native 端,则也是对相应的 handleMessage 进行处理,然后调用相应的组件来处理,如下是调用系统的控件:</p> <pre> <code class="language-java">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); };</code></pre> <p>通过这样复杂的工作,我们就可以完成大部分的工作。</p> <p> </p> <p>来自:http://www.phodal.com/blog/react-native-inside-cordova-webview-with-plugin/</p> <p> </p>