React Native 使用 react-native-webview 渲染 HTML

collin發表於2019-10-21

在 App 中,渲染 HTML 是一個非常常見的功能,有可能是直接渲染 HTML 字串或者是通過 URL 渲染遠端 HTML頁面。

React Native 提供了一個 WebView 元件以供我們實現 HTML 的渲染。

早先 WebView 是在 React Native 核心包中,後來為了減小 React Native 核心包的體積,便將其單獨提出到 react-native-webview元件中。

話不多說,讓我們來試試這個元件吧。

渲染遠端 HTML 內容

在開始之前,你需要使用 yarn/npm 來安裝它:

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>
        <WebView source={{ uri: 'https://www.baidu.com' }} />
      </View>
    )
  }
}

需要注意的是:第一次安裝完成後,需要重新執行一次 react-native run-* 讓 rn 幫我們自動 link 一次,否則會丟擲以下錯誤:

重新執行 react-native run-* 後,報錯沒了,但是頁面上一片空白:

這是什麼原因呢?在檢視了一下文件之後,發現 WebView 預設是不會自動設定其高度,所以這個時候高度是0,我們才無法在頁面中找到它。需要注意的是,如果 WebView 的父級元件是 View, 直接使用 style 給 WebView 元件設定高度是無效的,需要給這個 View 元件設定高度才能正常顯示。然而在 ScrollView 中,則是需要給 WebView 本身設定 height。其它幾個元件,例如 SafeAreaView 我沒有去嘗試,需要你自己親自去檢驗。

知道原因後,我們將 WebView 父元件設定 style 為 flex: 1,讓其鋪滿整個螢幕,在 reload 一次看看效果:

大功高程,我們已經成功的把百度展現到我們的 App 中了。

渲染 HTML 字串

有時候我們需要將 API 中返回的 HTML 字串解析出來。渲染 HTML 字串的時候,我們需要將 source 中的 uri 替換為 html,它的值就是我們的 HTML 字串。與 uri 不同的是,渲染 HTML 字串的時候,我們需要設定originWhitelist,比如可以設為["*"]來允許執行原生程式碼。

{/* ... */}
<WebView
  originWhitelist={['*']}
  source={{ html: `<h1>這裡是一個標題</h1>` }}
/>
{/* ... */}

到這裡我們就成功的把標題給渲染出來了。

根據內容自動計算高度

往往我們無法提前預知 HTML 內容的高度,所以我們無法給其設定一個固定的高度,而是需要根據內容來設定其高度。

為了達到這個目的,我們需要用到 injectedJavaScriptonMessage

injectedJavaScript

這個屬性的作用是設定一個在網頁載入之前執行的 js 程式碼。

onMessage

在 webview 內部的網頁中呼叫 window.postMessage 方法時可以觸發此屬性對應的函式,從而實現網頁和 RN 之間的資料交換。 設定此屬性的同時會在 webview 中注入一個 postMessage 的全域性函式並覆蓋可能已經存在的同名實現。

網頁端的 window.postMessage 只傳送一個引數 data,此引數封裝在 RN 端的 event 物件中,即 event.nativeEvent.data。data 只能是一個字串。

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>
    )
  }
}

從上圖可以看出我們的 WebView 已經根據其中內容動態的計算了其高度。但是還是有一個問題,這裡的文字太小了,要解決這個問題我們需要用到 meta - viewport

採用響應式佈局

這個其實很簡單,只需要在 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}
/>

總結

到此,關於 React Native 的基礎使用我們已經講完了,更多細節請參考以下連結:

原文地址

相關文章