需求背景
在移動端開發中,有的業務頁面使用原生平臺開發十分繁瑣,而使用H5頁面來實現則十分便捷和高效,這就是APP+H5混合開發。
在衆多APP當中也能看到H5混合開發的頁面。這個需求是十分常見的。
交互機制
react-native可以使用Webview組件來內嵌H5頁面,在開發過程中,H5頁面常常要和APP端進行數據交互。
那麼這個交互機制是怎麼樣的呢?原理如下:
- APP端注入JS腳本到H5端,供H5頁面調用。
- H5頁面調用APP注入的JS腳本的方法,傳遞事件和數據到APP端。
- APP端在Webview組件註冊onMessage回調事件,處理H5傳遞的事件和數據。
這個過程主要通過Webview組件的onMessage()方法和window.ReactNativeWebView.postMessage()方法來實現的。
- onMessage()方法詳解:https://reactnative.cn/docs/webview#onmessage
Demo截圖
- 藍色按鈕,是APP端的按鈕,點擊就可以傳遞事件和數據到H5頁面。
- 藍色按鈕下方則是H5頁面,裏邊有個按鈕,點擊該按鈕,則可以傳遞事件和數據到APP端。
- 這個就是React-native使用Webview內嵌H5頁面交互過程。
關鍵代碼
1. APP端注入JS腳本到H5端,供H5頁面調用。注意postMessage()方法的參數要轉爲JSON字符串。
const H5AppBridge = `
window.H5AppBridge={
sayHello:function(data){
let objData = {};
// 聲明事件類型。
objData.type='sayHello';
objData.data = data;
// 這裏注意要把data轉化爲JSON字符串,postMessage()只接受字符串參數。
window.ReactNativeWebView.postMessage(JSON.stringify(objData));
}
};
true;
`;
2. APP端傳遞事件和數據到H5頁面。注意H5方法的參數要轉爲JSON字符串。
const webView = this.refs['webview_ref'];
let params = {
msg: '這是從RN端傳來的消息'
};
// 注意:APP端傳遞參數到H5頁面,要將對象轉爲JSON字符串
let jsCode = `window.sayHello && window.sayHello(${JSON.stringify(params)});`;
// 調用H5端的方法,並傳遞數據
webView.injectJavaScript(jsCode);
3. H5頁面與APP端交互的JS代碼。
<script>
function sayHelloToApp(){
let data = {};
data.params={
msg:'從H5頁面發來的消息',
};
// 傳遞事件和數據到APP端
window.H5AppBridge.sayHello && window.H5AppBridge.sayHello(data);
}
// 這裏的方法是提供給APP端調用的
window.sayHello=function(data){
alert(JSON.stringify(data));
}
</script>
Demo完整代碼
import React, {Component} from 'react';
import {View, SafeAreaView, TouchableOpacity, Text} from 'react-native';
import {WebView} from 'react-native-webview';
const html = `
<html>
<head></head>
<style>
</style>
<body>
<h1>這是H5頁面</h1>
<button οnclick="sayHelloToApp()">sayHelloToApp</button>
<script>
function sayHelloToApp(){
let data = {};
data.params={
msg:'從H5頁面發來的消息',
};
// 傳遞事件和數據到APP端
window.H5AppBridge.sayHello && window.H5AppBridge.sayHello(data);
}
// 這裏的方法是提供給APP端調用的
window.sayHello=function(data){
alert(JSON.stringify(data));
}
</script>
</body>
</html>
`;
/**
* APP端注入JS腳本到H5端,供H5頁面調用。
* @type {string}
*/
const H5AppBridge = `
window.H5AppBridge={
sayHello:function(data){
let objData = {};
// 聲明事件類型。
objData.type='sayHello';
objData.data = data;
// 這裏注意要把data轉化爲JSON字符串,postMessage()只接受字符串參數。
window.ReactNativeWebView.postMessage(JSON.stringify(objData));
}
};
true;
`;
export default class WebviewH5 extends Component {
constructor(props) {
super(props);
}
render() {
return (
<SafeAreaView style={{flex: 1, paddingTop: 50,}}>
<TouchableOpacity
style={{
height: 40,
borderRadius: 20,
paddingLeft: 15,
paddingRight: 15,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#2988ff'
}}
onPress={() => {
const webView = this.refs['webview_ref'];
let params = {
msg: '這是從RN端傳來的消息'
};
// 注意:APP端傳遞參數到H5頁面,要將對象轉爲JSON字符串
let jsCode = `window.sayHello && window.sayHello(${JSON.stringify(params)});`;
// 調用H5端的方法,並傳遞數據
webView.injectJavaScript(jsCode);
}}
>
<Text>
sayHello
</Text>
</TouchableOpacity>
<WebView
ref={'webview_ref'}
source={{html: html}}
// 初始化webview注入全局代碼
injectedJavaScript={H5AppBridge}
domStorageEnabled={true}
scrollEnabled={true}
javaScriptEnabled={true}
showsVerticalScrollIndicator={false}
showsHorizontalScrollIndicator={false}
onMessage={event => {
this._handleMessage(event);
}}
/>
</SafeAreaView>
);
}
/**
*
* @param event
* @private
*/
_handleMessage = (event) => {
console.log("event.nativeEvent", event.nativeEvent);
const message = event.nativeEvent;
const webView = this.refs['webview_ref'];
try {
let objData = JSON.parse(message.data);
// let data = JSON.parse(objData.data);
let data = objData.data;
console.log("data", data);
switch (objData.type) {
case 'sayHello':
let params = data.params;
if (params) {
alert("sayHello:" + params.msg);
}
break;
}
} catch (e) {
alert("調用APP方法參數錯誤!參數爲:" + message.data);
}
};
}