react-native与webview的双向通信

react native 在官网的文档中,并没有详细介绍http://facebook.github.io/react-native/docs/webview.html#onmessage如何实现RN与WebView中JavaScript的通信。 关于 onMessage的描述如下,

onMessage?: function 
A function that is invoked when the webview calls window.postMessage. Setting this property will inject a postMessage global into your webview, but will still call pre-existing values of postMessage.
window.postMessage accepts one argument, data, which will be available on the event object, event.nativeEvent.data. data must be a string.

从上只能看出,RN可以接受来自于webview的消息,然而并无法得知如何从RN往Webview内部发送消息,读源码的时候,发现了 RN的webview组件中除了onMessage 还有个posMessage方法,由此可以推出能搞定RN与Webview内H5的双向通信https://github.com/facebook/react-native/blob/0150bc76eb8d2baa84ba040aead8131228c1185e/Libraries/Components/WebView/WebView.ios.js#L506

  • 在RN中 onMessage负责接收Weview的消息 postMessage 负责发送消息给Webview
  • 通过监听message事件来接收RN的消息,通过window.postMessage在Webview中向RN中发送消息

于是写代码验证下。在html中放置两个输入框A、B,点击按钮把A、B的值发送给RN端,返会两个数的和并展示

H5的代码

1
2
3
4
5
  <div style="padding-top:50px;">
<input id='A' />
<input id='B' />
</div>
<Button id='cacl'>计算</Button> <span>AB相加为</span><span id='sum'></span>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 var bt= document.getElementById('cacl');
bt.addEventListener('click',function(){
var valA = document.getElementById('A').value;
var valB = document.getElementById('B').value;
window.postMessage(JSON.stringify({
type:'add',
data:{
A:valA,
B:valB
}
}))
});
document.addEventListener('message',function(e){
document.getElementById('sum').innerText = JSON.parse(e.data).result;
})

RN 中的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
 export default class RNWebbViewMessage extends Component {
constructor(props){
super(props);
this.onMessage = this.onMessage.bind(this);
}
onMessage(e){
var event =e.nativeEvent;
var data=JSON.parse(event.data);
if(data.type ==='add'){
let args= data.data;
let a = Number(args.A);
let b = Number(args.B);
this.refs.webviewRef.postMessage(JSON.stringify({
result:a+b
}))
}
}
render() {
return (
<View style={{flex:1}}>
<WebView ref={'webviewRef'} automaticallyAdjustContentInsets={false} onMessage={this.onMessage} javaScriptEnabled={true} source={{uri:'http://localhost/work/study/rnwebviewmessage/index.html?ss'}} />
</View>
);
}
}

经过验证结论是成立的。

  • webview网页可以使用window.postMessage 发送消息, document.addEventListener('message',callback)来监听消息
  • RN 中onMessage 通过 参数中nativeEvent.data获得webview传递过来的字符串。然后把结果通过webview实例的postMessage方法发送到H5中

以上只是通信api的介绍,如果生产环境中使用的话,还可以对调用形式进一步封装,比如H5中方法调用RN中的分享,并且要求知道分享结果是否成功,可以使用 webviewBridgecallRN('share',{title:'微信分享',content:"分享内容"},callback)这种类似的方式,同时RN中可根据事件的名字进行分模块来实现。

备注

React.createClass VS React.Component

创建react组件有两种方式,使用用 React.createClass 以及用es6模块化的方式extends React.Component 通过集成 Component 组件类的形式来实现。两者的作用类似,后者是新的语法也是趋势。于是赶紧对比下如何升级到新的语法

React.createClass

creatClass创建的组件赋值为const类型的变量,在组件内部实现必要的 render函数

import React from 'react';
const Contacts = React.createClass({
  render() {
    return (
      <div></div>
    );
  }
});
export default Contacts;

React.Component

通过ES6的类继承模式在 构造函数里面要 先通过super(props)调用父组件的构造函数

import React from 'react';
class Contacts extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    return (
      <div></div>
    );
  }
}
export default Contacts;

propTypes and getDefaultProps

React.createClass

import React from 'react';
const Contacts = React.createClass({
  propTypes: {

  },
  getDefaultProps() {
    return {

    };
  },
  render() {
    return (
      <div></div>
    );
  }
});
export default Contacts;

React.Component

给类增加属性的方式发生了变化,defaultProps 来替代 getDefaultProps() 似乎简单了不少

import React from 'react';
class Contacts extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    return (
      <div></div>
    );
  }
}
Contacts.propTypes = {
};
Contacts.defaultProps = {
};
export default Contacts;

状态定义 State

import React from 'react';
const Contacts = React.createClass({
  getInitialState () {
    return {

    };
  },
  render() {
    return (
      <div></div>
    );
  }
});
export default Contacts;

定义默认的状态在构造函数中实现来取代了 getInitialState,语法更简单

import React from 'react';
class Contacts extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
    };
  }
  render() {
    return (
      <div></div>
    );
  }
}
export default Contacts;

this的含义不同

React.createClass中,给组件绑定事件的函数中的 this 指向当前的组件 ,在 React.Component 中事件函数中的 this 默认指向为空

import React from 'react';
const Contacts = React.createClass({
  handleClick() {
    console.log(this); // 但前的组件实例
  },
  render() {
    return (
      <div onClick={this.handleClick}></div>
    );
  }
});
export default Contacts;

import React from 'react';
class Contacts extends React.Component {
  constructor(props) {
    super(props);
  }
  handleClick() {
    console.log(this); // null
  }
  render() {
    return (
      <div onClick={this.handleClick}></div>
    );
  }
}
export default Contacts;

可以在绑定事件的时候使用 JS中的bind来绑定this 对象 <div onClick={this.handleClick.bind(this)}></div>也可以在 构造函数里面手动的 bind this.handleClick = this.handleClick.bind(this);

import React from 'react';
class Contacts extends React.Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
  }
  handleClick() {
    console.log(this); // React Component instance
  }
  render() {
    return (
      <div onClick={this.handleClick}></div>
    );
  }
}
export default Contacts;

Mixins

使用mixins可以提高复用性,React.Component 不再支持 Mixins

import React from 'react';
var SomeMixin = {
  doSomething() {
  }
};
const Contacts = React.createClass({
  mixins: [SomeMixin],
  handleClick() {
    this.doSomething(); // use mixin
  },
  render() {
    return (
      <div onClick={this.handleClick}></div>
    );
  }
});
export default Contacts;

在 React.Component 官方推荐 HOC(高阶组件)的形式 来替换,如果一定要使用 mix的话 也可以使用 react-mixin