React Native 是目前流行的跨平台移动应用开发框架之一。通过采用不同的方法进行混合移动应用开发,它不会生成原生 UI 组件,而是基于 React,React Native 是一个用于构建基于 Web 的交互界面的 JavaScript 库,因此会有更丰富的 UI 体验效果,同时也能够很好地调用底层框架的UI使用。 React Native 已经成为一种流行的移动开发技术,它提供了一个使用 JavaScript 构建原生跨平台移动应用的强大框架。
在做原生开发时,一般我们会有一些加载H5网页的需求,或者需要内嵌类似vue等前端框架开发的web 页面。我们知道在Android 或者 ios中实现这个功能的控件是WebView,在ReactNative中也有实现此类需求的组件,它的名字也是WebView,后来单独集成为第三方组件 react-native-webview。
1. ReactNative 页面引用 WebView
react-native-webview 组件的安装:
npm i react-native-webview
react-native-webview 组件引用:
import { WebView } from 'react-native-webview';
WebView 标签内渲染 vue 页面,该标签下的配置信息如下:
<WebView ref={ (webView) => this.webView = webView }
originWhitelist={ ['*'] }
// 布尔值,指定WebView中是否启用JavaScript。只在Android上使用,因为在iOS上默认启用了JavaScript。
javaScriptEnabled={ true }
// 布尔值,指定是否开启DOM本地存储
domStorageEnabled={ true }
// 允许文件上传
allowFileAccess={ true }
// 在webView内部网页中,调用window.postMessage可以触发此属性对应的函数,通过event.nativeEvent.data获取接收到的数据,实现网页和RN之间的数据传递
onMessage={ this._onMessage }
//初始化调用方法
onLoad={() => { this.handleInjectJavascript();}}
// 加载时强制使用loading转圈视图,如果为true,webview可能会加载失败,显示为空白
startInLoadingState={false}
// webview加载错误页面
renderError={this.renderErrorView}
// 网络路径
// 也可以为本地路径 source={ {uri: 'http://192.168.1.1111:8080/'} }
source={ {uri: 'http://www.baidu.com/'} }
/>
2. ReactNative 与 vue交互
2.1 RN —> Vue
RN 页面在webview初始化source前,通常是调用一个方法,执行的函数是 onLoad 函数
onLoad={() => { this.handleInjectJavascript('hello world'); }}
调用的 onLoad 函数 中的 handleInjectJavascript() 方法才是真正用于给 Vue 或者 Html 页面发送数据的函数方法:
//RN webview 向 vue发送数据
handleInjectJavascript = (data) => {
// 拼接数据为方法
const injectJavascriptStr = `(function() {
window.WebViewBridge.onMessage(${JSON.stringify(data)});
})()`;
// 通过 injectJavaScript 将数据传递给WebView页面,并立即执行为js
if(this.webView) {
this.webView.injectJavaScript(injectJavascriptStr)
}
}
vue 页面接受数据:
mounted() {
window.WebViewBridge = {
onMessage: this._onMessage //在window上挂载一个onMessage方法,RN会调用
}
// 自定义事件后直接触发:
const event = new Event('WebViewBridge')
window.dispatchEvent(event);
},
methods: {
// 接收 RN 发送的消息
_onMessage(data) {
let that = this;
console.log('data ------- ',JSON.stringify(data)); // 'hello world'
}
}
以上是RN向vue 传值的过程,其具体过程:RN中给 window.WebViewBridge 增加一个名为 onMessage 的方法,vue初始化页面会对应给出onMessage 指向的方法,在此代码块 指向 _onMessage 方法并执行。
2.2 Vue —> RN
vue 页面发送数据:
mounted() {
// 自定义事件后直接触发:
const event = new Event('WebViewBridge');
window.dispatchEvent(event);
},
methods: {
// 向rn发送消息, 将值 'hello world' 挂载到 postMessage
_postMessage('hello world') {
window.ReactNativeWebView.postMessage(data);
}
}
RN 页面在接收到 vue 传输的数据之后执行 onMessage 函数方法
onMessage={ this._onMessage }
触发 _onMessage 方法获取 Vue 或者 Html 页面返回的数据
// 接受vue发送来的消息。 -- '接收vue发来的消息onMessage hello world'
_onMessage = (event) => {
console.log('接收vue发来的消息onMessage', event.nativeEvent.data);
}
2.3 Vue <—> RN (多次双向传值)
vue 页面:
mounted() {
window.WebViewBridge = {
onMessage: this._onMessage,
receiveMessage: this._receiveMessage //在window上挂载一个receiveMessage方法,RN自行调用
}
const event = new Event('WebViewBridge')
window.dispatchEvent(event);
},
methods: {
// 向rn发送消息
_postMessage('wow,RN!!') {
window.ReactNativeWebView.postMessage(data); // 将值 'wow,RN!!' 挂载到 postMessage
},
// 二次或多次接收RN发送消息
_receiveMessage(data){
let that = this;
console.log('data receiveMessage------- ',JSON.stringify(data));
}
}
RN 页面在接收到 vue 传输的数据之后执行 onMessage 函数
onMessage={ this._onMessage }
// 接受vue发送来的消息
_onMessage = (event) => {
console.log('接收vue发来的消息onMessage', event.nativeEvent.data);
// '接收vue发来的消息onMessage wow,RN!!'
const injectJavascriptStr = `(function() {
window.WebViewBridge.receiveMessage(${JSON.stringify('hello,vue2!!! ')});
})()`;
this.webView.injectJavaScript(injectJavascriptStr);
}
3. react-native-webview 组件使用总结
使用 WebView 组件我们可以通过 url 来加载显示一个网页,也可以传入一段 html 代码来显示。该组件的引入是解决 React Native 内嵌 Vue 的比较好的解决方案,该组件自带的常用属性和方法我们可以归纳如下:
3.1 主要属性:
- source:在 WebView 中载入一段静态的 html 代码或是一个 url(还可以附带一些 header 选项)
- automaticallyAdjustContentInsets:设置是否自动调整内容。格式:boolean
- contentInset:设置内容所占的尺寸大小。格式:{top:number,left:number,bottom:number,right:number}
- injectJavaScript:当网页加载之前注入一段 js 代码。其值是字符串形式。
- startInLoadingState:是否开启页面加载的状态,其值为 true 或者 false。
- bounces(仅iOS):回弹特性。默认为 true。如果设置为 false,则内容拉到底部或者头部都不回弹。
- scalesPageToFit(仅iOS):用于设置网页是否缩放自适应到整个屏幕视图,以及用户是否可以改变缩放页面。
- scrollEnabled(仅iOS):用于设置是否开启页面滚动。
- domStorageEnabled(仅Android):用于控制是否开启 DOM Storage(存储)。
- javaScriptEnabled(仅Android):是否开启 JavaScript,在 iOS 中的 WebView 是默认开启的。
3.2 主要方法:
- onNavigationStateChange:当导航状态发生变化的时候调用。
- onLoadStart:当网页开始加载的时候调用。
- onError:当网页加载失败的时候调用。
- onLoad:当网页加载结束的时候调用。
- onLoadEnd:当网页加载结束调用,不管是成功还是失败。
- renderLoading:WebView组件正在渲染页面时触发的函数,只有 startInLoadingState 为 true 时该函数才起作用。
- renderError:监听渲染页面出错的函数。
- onShouldStartLoadWithRequest(仅iOS):该方法允许拦截 WebView 加载的 URL 地址,进行自定义处理。该方法通过返回 true 或者 false 来决定是否继续加载该拦截到请求。
4. react-native-webview 组件基于RN 使用样例
4.1 场景一:直接展示外源网页信息
import React,{Component} from 'react'
import {
StyleSheet,
View,
Text,
WebView,
} from 'react-native'
export default class Root extends Component{
constructor(props){
super(props)
}
render(){
return (
<View style={styles.container}>
<WebView
source={{uri: 'https://www.baidu.com'}}
domStorageEnabled={true}
javaScriptEnabled={true}
startInLoadingState={true}
automaticallyAdjustContentInsets={true}>
</WebView>
</View>
)
}
}
const styles = StyleSheet.create({
container:{
flex:1,
},
})
程序启动后,我们使用 WebView 加载 baidu.com 并全屏显示,局部效果如下:
4.2 场景二:组件在实际登陆场景中的使用
我们有如下场景:一款 toB 的 app,面对很多家客户,每家客户都有自己的登录系统界面,并且客户要求接入自己的登录系统。这个时候就需要 webview 接入登录界面之后,进行一些 “值拦截”,进行登录状态判断。这里主要用到 webview 的 onNavigationStateChange 参数。此方法会在当导航状态发生变化的时候调用,例如取到下面的参数:
{
"canGoBack": false,
"canGoForward": false,
"loading": true,
"navigationType": "other",
"target": 229,
"title": "",
"url": "https://www.baidu.com/"
}
这里主要注意 url 字段,网页内部登录完成之后,可以发起一个重定向,前端关注 url 变化,进行登录状态的判断,同时重定向的 url 中可以拼接一些登录信息字段,用于前端登录状态校验等,例如:
{ "url": "https://www.baidu.com ? sessionId=xxx & username= xxx" }
4.3 场景三:功能模块嵌入到 RN 中
将 webview 作为功能模块载体,这个时候就会涉及到用户交互,需要 rn 与 h5 进行值的互相传递。此时就要react native 向 web 中注入 js,web 也可以主动回传数据到 react native中。webview 组件提供了 injectedJavaScript 用于向 h5 注入js,在 h5 中使用 window.ReactNativeWebView.postMessage 进行主动的回调,并且在 webview 的 onMessage 中进行字段的监听,以此就可以完成 react 和 h5 之间的数据交互。
const injectJSStr = `
window.injectStr='我是注入的字段';
var div = document.getElementById('testID');
div.style.color='red';
div.style.fontSize='100px';
`;
const html = `
<html>
<head></head>
<body>
<script>
setTimeout(function () {
window.ReactNativeWebView.postMessage(window.injectStr)
}, 2000)
</script>
<div id='testID'>Hello Word</div>
</body>
</html>
`;
......
<View style={{flex: 1}}>
<WebView
source={{html}}
injectedJavaScript={injectJSStr}
onMessage={event => {
alert(event.nativeEvent.data);
}}
/>
</View>