react-native-webview 渲染html

缑文栋
2023-12-01

react-native-webview 渲染html

git : https://github.com/react-native-webview/react-native-webview

react-native: https://reactnative.cn/docs/webview#nativeconfig

功能:

在APP中,渲染html

安装

npm install react-native-webview
yarn add react-native-webview

简单使用

import React, { Component } from 'react';
import { View } from 'react-native';
import { WebView } from 'react-native-webview';

export default class App extends Component {
  render() {
    return (
      <View style={{flex:1}}>
        <WebView source={{ uri: 'https://www.baidu.com' }} />
      </View>
    )
  }
}

渲染html字符串

{/* ... */}
<WebView
  originWhitelist={['*']}
  source={{ html: `<h1>这里是一个标题</h1>` }}
/>
{/* ... */}

属性

  • automaticallyAdjustContentInsets

    控制是否调整放置在导航条、标签栏或工具栏后面的web视图的内容。默认值为true

  • contentInset {top: number, left: number, bottom: number, right: number}

    设置网页内嵌边距

  • injectedJavaScript

    这个属性的作用是设置一个在网页加载之前执行的 js 代码。

  • mediaPlaybackRequiresUserAction

    设置页面中的HTML5音视频是否需要在用户点击后再开始播放。默认值为true

  • scalesPageToFit

    设置是否要把网页缩放到适应视图的大小,以及是否允许用户改变缩放比例。

  • source 常用

    在WebView中指定加载内容html或者url,可以指定header,method等

  • originWhitelist 属性

    webview中唤起app

// 唤起支付宝、支付宝香港、云闪付
<WebView
    originWhitelist={['http://*', 'https://*', 'intent://*', 'alipays://*', 'alipayhk://*', 'uppaywallet://']}
/>
  • startInLoadingState

    强制WebView在第一次加载时先显示loading视图。默认为true

  • domStorageEnabled(android)

    布尔值,指定是否开启DOM本地存储

  • javaScriptEnabled(android)

    布尔值,指定WebView中是否启用JavaScript。只在Android上使用,因为在iOS上默认启用了JavaScript。

  • mixedContentMode(android)

    指定混合内容模式。即WebView是否应该允许安全链接(https)页面中加载非安全链接(http)的内容,

    • ‘never’ (默认) - WebView不允许安全链接页面中加载非安全链接的内容

    • ‘always’ - WebView允许安全链接页面中加载非安全链接的内容。

    • ‘compatibility’ - WebView会尽量和浏览器当前对待此情况的行为一致

  • userAgent(android)

    为WebView设置user-agent字符串标识。这一字符串也可以在原生端用WebViewConfig来设置,但js端的设置会覆盖原生端的设置。

  • allowsInlineMediaPlayback(ios)
    指定HTML5视频是在网页当前位置播放还是使用原生的全屏播放器播放。 默认值为false,视频在网页播放还需要设置webkit-playsinline

  • bounces(ios)
    指定滑动到边缘后是否有回弹效果。

  • decelerationRate(ios)
    指定一个浮点数,用于设置在用户停止触摸之后,此视图应以多快的速度停止滚动。也可以指定预设的字符串值,如"normal"和"fast",

  • scrollEnabled(ios)
    是否启用滚动

函数

  • injectJavaScript
    函数接受一个字符串,该字符串将传递给WebView,并立即执行为JavaScript

  • onError
    加载失败时回调

  • onLoad
    完成加载时回调

  • onLoadEnd
    加载成功或者失败都会回调

  • onLoadStart
    开始加载的时候回调

  • onMessage 实现网页和RN之间的数据传递

    在 webview 内部的网页中调用 window.postMessage 方法时可以触发此属性对应的函数,从而实现网页和 RN 之间的数据交换。 设置此属性的同时会在 webview 中注入一个 postMessage 的全局函数并覆盖可能已经存在的同名实现。

    网页端的 window.postMessage 只发送一个参数 data,此参数封装在 RN 端的 event 对象中,即 event.nativeEvent.data。data 只能是一个字符串。

  • renderError
    返回一个视图用来提示用户错误

  • renderLoading
    返回一个加载指示器

    renderLoading={() => {
     return <View><Text>这是自定义Loading...</Text></View>
     }}
    
  • onShouldStartLoadWithRequest(ios)
    请求自定义处理,返回true或false表示是否要继续执行响应的请求

  • onNavigationStateChange

    在webView状态发生改变的时候回调webView的event信息,event信息里包含了url, title, loading, canGoBack, canGoForward.

html的引入不同方式

        <WebView
        //不同的source情况
        // source={{ uri: 'http://johnyu.cn' }}
        // source={{ html: '<h1>Hello world</h1>' }}
        // source={require('./index.html')} //此方式不会进行html解析
        //文件位于android/src/main/assets/下
        source={{uri:'file:///android_asset/index.html'}}
        injectedJavaScript={runFirst} 
        onMessage={(this._receive)}
      />

例子1 自适应高度

import React, { Component } from 'react';
import { View, ScrollView, SafeAreaView } from 'react-native';
import { WebView } from 'react-native-webview';

export default class App extends Component {
  constructor(props) {
    super(props);

    this.state = { webViewHeight: 0 };
  }

  {/* 根据内容计算 WebView 高度 */}
  onWebViewMessage = (event) => {
    this.setState({ webViewHeight: Number(event.nativeEvent.data) });
  }

  render() {
    return (
      <View style={{ flex: 1 }}>
        <View style={{ height: 100, backgroundColor: 'yellow' }}></View>
        <View style={{ height: this.state.webViewHeight }}>
          <WebView
            originWhitelist={['*']}
            source={{ html: `<p>这里是一个标题</p>` }}
            injectedJavaScript='window.ReactNativeWebView.postMessage(document.documentElement.scrollHeight)'
            onMessage={this.onWebViewMessage}
          />
        </View>
        <View style={{ height: 100, backgroundColor: 'green' }}></View>
      </View>
    )
  }
}

例子2 采用响应式布局

这个其实很简单,只需要在 header 中设置 meta 标签即可。

<WebView
  originWhitelist={['*']}
  source={{ html: `
  <html>
    <head>
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
    </head>

    <body>
      <p>这里是一个标题</p>
    </body>
  </html>
  ` }}
  injectedJavaScript='window.ReactNativeWebView.postMessage(document.documentElement.scrollHeight)'
  onMessage={this.onWebViewMessage}
/>

例子3 一次完整的web与RN通信

html

<!DOCTYPE html>
<html lang="en">
<head>
    <title></title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<div style="text-align: center">
    <button id="button">发送数据到react native</button>
    <p style="text-align: center">收到react native发送的数据: <span id="data"></span></p>
</div>
<script>
    var data = 0;

    function sendData(data) {
        if (window.originalPostMessage) {
            window.postMessage(data);
        } else {
            throw Error('postMessage接口还未注入');
        }
    }

    window.onload = function () {
        document.addEventListener('message', function (e) {
            document.getElementById('data').textContent = e.data;
        });
        document.getElementById('button').onclick = function () {
            data += 100;
            sendData(data);
        }
    }
</script>
</body>
</html>

RN

import React from 'react';
import {
    View,
    Text,
    StyleSheet,
    WebView
} from 'react-native';

export default class Web extends React.Component {

    constructor(props) {
        super(props);
        this.state = {
            webViewData: ''
        }
        this.data = 0;
    }

    sendMessage() {
        this.refs.webview.postMessage(++this.data);
    }

    handleMessage(e) {
        this.setState({webViewData: e.nativeEvent.data});
    }

    render() {
        return (
            <View style={styles.container}>
                <View style={{width: 375, height: 220}}>
                    <WebView
                        ref={'webview'}
                        source={require('./index.html')}
                        style={{width: 375, height: 220}}
                        onMessage={(e) => {
                            this.handleMessage(e)
                        }}

                    />

                </View>
                <Text>来自webview的数据 : {this.state.webViewData}</Text>
                <Text onPress={() => {
                    this.sendMessage()
                }}>发送数据到WebView</Text>
            </View>
        )
    }
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        marginTop: 22,
        backgroundColor: '#F5FCFF',
    },

});

注:

rn向html发送数据,html接收

rn向html发生数据可以通过postMessage函数实现。如下
sendMessage() {
    this.refs.webview.postMessage(++this.data);
}

//在html中注册事件接收rn发过来的数据并显示在html中
 
document.addEventListener('message', function(e) {
   document.getElementById('data').textContent = e.data;
});

html向rn发送数据,rn接收

在html中我们定义了一个按钮,并添加事件向rn发送数据

//window.postMessage向rn发送数据
document.getElementsByTagName('button')[0].addEventListener('click', function() {
   window.postMessage('这是html发送到RN的消息');
});

当html中调用了window.postMessage函数后,WebView的onMessage函数将会被回调,用来处理html向rn发送的数据,可以通过e.nativeEvent.data获取发送过来的数据。
 
//接收HTML发出的数据
 
_onMessage = (e) => {
    this.setState({
      message: e.nativeEvent.data,
    })
}

例子4 网页端调用通过url schema进行跳转被当成url访问的情况

<WebView
    // 配置白名单,确保能够正常唤起app
    originWhitelist={['http://*', 'https://*', 'intent://*', 'alipays://*', 'alipayhk://*', 'uppaywallet://']}
    onShouldStartLoadWithRequest={this.onShouldStartLoadWithRequest}
/>

onShouldStartLoadWithRequest=(request)=>{
        // short circuit these
        if (!request.url ||
          request.url.startsWith('http') ||
          request.url.startsWith("/") ||
          request.url.startsWith("#") ||
          request.url.startsWith("javascript") ||
          request.url.startsWith("about:blank")
        ) {
          return true;
        }
      
        // blocked blobs
        if(request.url.startsWith("blob")){
          Alert.alert("Link cannot be opened.");
          return false;
        }
      
        // list of schemas we will allow the webview
        // to open natively
        if(request.url.startsWith("tel:") ||
          request.url.startsWith("mailto:") ||
          request.url.startsWith("maps:") ||
          request.url.startsWith("geo:") ||
          request.url.startsWith("alipays:") ||
          request.url.startsWith("alipayhk:") ||
          request.url.startsWith("uppaywallet:") ||
          request.url.startsWith("sms:")
          ){
      
          Linking.openURL(request.url).catch(er => {
            Alert.alert("Failed to open Link: " + er.message);
          });
          return false;
        }
      
        // let everything else to the webview
        return true;
      }

 类似资料: