乐趣区

关于react-native:reactnative中webview的通信桥梁irdRnBridge

背景介绍:

前段时间花了一些工夫去钻研 react-native 中 webview 的通信机制,理解到当中的原理,正好业务中也遇到了 rn 中 webview 内嵌 h5 的页面,发现尽管 rn 提供了一套相似 js 的 postmessage 机制,但在 h5 和 rn 之间的通信中往往仅靠这个机制是很难进步开发效率和升高代码的复杂度。
因而基于晋升在 rn 中开发 h5 效率的原因下,开发了这个 ird-RnBridge。这个库从往年 5 月份开始构思,设计,而后五月份底实现了 v1.0.0,之后在七月份进行了迭代开发,并实现了 v1.1.0 的功能性版本迭代,足以能反对很多场景的开发和调试。

版本迭代历史

简略介绍:

该桥梁次要是实用在 react-native 和 h5 之间的通信场景下,并且在 rn 侧和 h5 侧各提供了一套不雷同的 api 办法汇合以便调用。它提供了几个方面的性能:

1) 安全性校验建设桥梁:
因为 rn 的很多原生性能是通过 webview 提供给 h5 页面调用,如果不甄别嵌入在 webview 中的 h5 页面是否平安,而间接全副提供,这会带来很多安全性的问题。因而 rnbridge 采取了双重校验形式:
首先:h5 侧必须调用 checkSafety 发动建设桥梁的申请,rn 会对其发过来的申请进行校验,该校验会交给 rn 侧解决,如果解决通过,则 rnbridge 会发送一个 token 值给到 h5 侧,以表明桥梁建设胜利;
其次:每次 h5 和 rn 的通信,都会带上该 token 值,rnbridge 在 rn 侧会对其进行匹配,不统一会禁止调用。

h5 侧:

      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 侧:

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

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

如果不定义 checksafeCheck,则 rnbridge 主动认为是通过,从而主动建设桥梁。

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

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

console:

ajax:

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

加载性能参数:

资源性能参数:

原理介绍:

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

  • 平安校验,建设桥梁:

  • h5 调用 rn 办法:

  • rn 调用 h5 办法:

用处介绍:

因为 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 以作激励。

退出移动版