react-native中webview的通讯桥梁:ird-RnBridge

背景介绍:

前段时间花了一些时间去研究react-native中webview的通讯机制,了解到当中的原理,正好业务中也遇到了rn中webview内嵌h5的页面,发现虽然rn提供了一套相似js的postmessage机制,但在h5和rn之间的通讯中每每仅靠这个机制是很难提升开发效率和下降代码的复杂度。
所以基于提高在rn中开发h5效率的原因下,开发了这个ird-RnBridge。react

版本迭代历史git

简单介绍:

该桥梁主要是适用在react-native和h5之间的通讯场景下,而且在rn侧和h5侧各提供了一套不相同的api方法集合以便调用。它提供了几个方面的功能:github

1) 安全性校验创建桥梁:
因为rn的不少原生功能是经过webview提供给h5页面调用,若是不鉴别嵌入在webview中的h5页面是否安全,而直接所有提供,这会带来不少安全性的问题。所以rnbridge采起了双重校验方式:
首先:h5侧必须调用checkSafety发起创建桥梁的请求,rn会对其发过来的请求进行校验,该校验会交给rn侧处理,若是处理经过,则rnbridge会发送一个token值给到h5侧,以代表桥梁创建成功;
其次:每次h5和rn的通讯,都会带上该token值,rnbridge在rn侧会对其进行匹配,不一致会禁止调用。 web

h5侧:ajax

RnBridge.checkSafety({demo: 'demo'}, (data) => {
          document.getElementById('demo').style.color = 'blue';
          console.log('bridge success:', data);
          RnBridge.getSessionStore(['sat2'], (data) => {
              const content = document.getElementById('content');
              content.innerText = JSON.stringify(data);
              console.log('data1', data);
          })
      });

rn侧:npm

RnBridge.initWebview(this.webview, {
    checkSafety: (params, send) => {
       this.veritySafety(params, send);
    },
});

veritySafety(params, send) {
    send({isSuccess: true, result: 'welcome'});
}

若是不定义checksafeCheck,则rnbridge自动认为是经过,从而自动创建桥梁。react-native

2) rn侧和h5侧相互通讯:
rn侧能够经过initWebview来注册提供给h5侧调用的api方法集合,当创建了桥梁之时,rnbridge会将注册在initWebview中的方法名的集合发送到h5侧,h5侧只须要直接调用invokeRN就能够调用rn侧的方法。同理rn侧也是。api

3) 提供方便的调试方式:
由于h5内嵌在rn的webview之中,h5内的ajax和console都只能经过vconsole这个插件上看到,调试起来不怎么方便。因此rnbridge提供了debug方法并提供了console和ajax两种模式,能够直接将h5中的ajax和console直接在浏览器上看和调试。浏览器

console:
image.png安全

ajax:
image.png

4) 提供h5侧加载资源的性能参数:
webview加载h5以及h5和rn创建桥梁等参数能够经过sendPerformance和sendPerformanceByType来发起,并最终能够在rn层获取到h5的资源性能参数。

加载性能参数:
image.png

资源性能参数:
image.png

原理介绍:

这里大概分享一下rnbridge在桥梁创建和互相通讯之间的一些设计原理:

  • 安全校验,创建桥梁:

image.png

  • h5调用rn方法:

image.png

  • rn调用h5方法:

image.png

用途介绍:

由于rnbridge在rn侧和h5侧各自都拥有一套api方法集合,因此在调用这些方法以前,须要调用rnbridge的switchMode方法,从而选择对应的一套api方法集合;

例如在h5侧:

RnBridge.switchMode({mode: 'h5'});

此时h5的api集合就会注入到window.RnBridge之中,所以能够全局范围内随意调用。

在rn侧:

RnBridge.switchMode({mode: 'rn'});

此时rn的api集合就会注入到RnBridge自己,因此能够经过RnBridge来调用。

这是一个简单的例子:
h5侧:

RnBridge.switchMode({mode: 'h5'});
 
 RnBridge.initH5({
    h1: (params, send) => {
      send({isSuccess: true, result: {a2: 39}});
    },
    h2: (params, send) => {
      send({isSuccess: false, result: {a1: 21}});
    }
});

RnBridge.checkSafety({demo: 'demo'}, (data) => {
    document.getElementById('demo').style.color = 'blue';
});

RnBridge.invokeRN({
    method: 'a1',
    params: {a1: 12},
    success: (result) => {
        document.getElementById('demo').style.color = 'green';
        document.getElementById('demo').innerText = JSON.stringify(result);
    },
   fail: (result) => {
        document.getElementById('demo').style.color = 'red';
        document.getElementById('demo').innerText = JSON.stringify(result);
    }
});

rn侧:

RnBridge.switchMode({mode: 'rn'});

export class Demo extends React.Component {
    constructor(props) {
        super(props);
        this.webview = null;
    }

    render() {
        return (
            <View style={{flex: 1}}>
                <WebView
                    originWhitelist={['*']}
                    source={{ uri: 'http://192.168.1.101:9001/'}}
                    ref={ele => this.webview = ele}
                    onMessage={(e) => {
                        console.log('e', e.nativeEvent.data);
                        RnBridge.listenH5(e.nativeEvent.data);
                    }}
                    onError={(e) => {
                        console.log('error', e)
                    }}
                    onLoadEnd={() => {
                        console.log('load end')
                    }}
                    onLoadStart={() => {
                        console.log('load start')
                    }}
                />
                <View>
                    <Text onPress={() => {
                        this.handleC()
                    }}>click</Text>
                </View>
            </View>
        )
    }

    componentDidMount() {
        console.log('RnBridge', RnBridge);
        RnBridge.initWebview(this.webview, {
            a1: (params, send) => {
                this.handleA(params, send);
            },
            a2: (params, send) => {
                this.handleB(params, send);
            },
            checkSafety: (params, send) => {
                this.veritySafety(params, send);
            },
            setTitle: (params, send) => {
                console.log('title', params);
            }
        })
    }

    veritySafety(params, send) {
        send({isSuccess: true, result: 'welcome'});
    }

    handleA(params, send) {
        console.log(params);
        send({isSuccess: false, result: 'asdf'})
    }

    handleB(params, send) {
        console.log(params);
        RnBridge.invokeH5({

        });
        setTimeout(() => {
            send({isSuccess: true, result: 'ok'})
        }, 1000);
    }

    handleC () {
        RnBridge.invokeH5({
            method: 'h3',
            params: {demo: true},
            success: (params) => {
                console.log('params', params);
            },
            fail: (params) => {
                console.log('fail', params);
            }
        })
    }
}

详细的适用介绍能够查看这里:
ird-rnbridge的readme

结尾:

这个工具库是利用本人晚上加班回来后,花休息时间来设计和开发,所以rnbridge可能存在一些不足之处,若有不足之处请提出,本人会利用业余时间持续优化和功能迭代;开源不易,且行且互勉,若以为勉强还行,请在github中点个star以做鼓励。

相关文章
相关标签/搜索