Blog

Blog

PHODAL

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);
};

通过这样复杂的工作,我们就可以完成大部分的工作。

关于我

Github: @phodal     微博:@phodal     知乎:@phodal    

微信公众号(Phodal)

围观我的Github Idea墙, 也许,你会遇到心仪的项目

QQ技术交流群: 321689806
comment

Feeds

RSS / Atom

最近文章

关于作者

Phodal Huang

Engineer, Consultant, Writer, Designer

ThoughtWorks 技术专家

工程师 / 咨询师 / 作家 / 设计学徒

开源深度爱好者

出版有《前端架构:从入门到微前端》、《自己动手设计物联网》、《全栈应用开发:精益实践》

联系我: h@phodal.com

微信公众号: 最新技术分享

标签