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以做鼓勵。

相關文章
相關標籤/搜索