1. 程式人生 > >React Native 中使用 WebView 載入本地 html

React Native 中使用 WebView 載入本地 html

1. 主要屬性

  • source:在 WebView 中載入一段靜態的 html 程式碼或是一個 url(還可以附帶一些 header 選項)
  • automaticallyAdjustContentInsets:設定是否自動調整內容。格式:bool
  • 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 是預設開啟的。

2.主要方法

  • onNavigationStateChange:當導航狀態發生變化的時候呼叫。
  • onLoadStart:當網頁開始載入的時候呼叫。
  • onError:當網頁載入失敗的時候呼叫。
  • onLoad:當網頁載入結束的時候呼叫。
  • onLoadEnd:當網頁載入結束呼叫,不管是成功還是失敗。
  • renderLoading:WebView元件正在渲染頁面時觸發的函式,只有 startInLoadingState 為 true 時該函式才起作用。
  • renderError:監聽渲染頁面出錯的函式。
  • onShouldStartLoadWithRequest(僅iOS):該方法允許攔截 WebView 載入的 URL 地址,進行自定義處理。該方法通過返回 true 或者 falase 來決定是否繼續載入該攔截到請求。

3.用法詳解

ReactNative 中的 

WebView 可以載入一個外部網頁,例如下面官網的例子

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

class MyWeb extends Component {
  render() {
    return (
      <WebView
        source=
        style=
      />
    );
  }
}

有時候可以使用 WebView 彌補一些 ReactNative 內建的元件實現不了的東西,我們可以藉助 HTML 來完成,畢竟 HTML 有豐富的工具可以用。例如要想在 ReactNative 裡展示圖表,原生自帶的元件則沒辦法實現,其他的圖表元件都是基於 react-native-svg 實現的,展示效果目前還不足人意。這個時候 HTML 則有一大堆圖表工具可以使用,echarts, highcharts 等等等。

接下來我們可以寫一個網頁,然後使用 uri 外部引用進來。但是把網頁放在伺服器上,然後 App 引用還是挺囉嗦的。能不能把網頁放在應用內部,本地引用呢?當然可以,這個才是這篇部落格的主題。

WebView 載入本地 html

假設我們有一個 demo 專案

Demo/
    android/
    ios/
    index.android.js
    index.ios.js
    packege.json
    WebViewScreen.js
    pages/
      demo.html
    ...

新建一個檔案 WebViewScreen.js

import React from 'react';
import { WebView } from 'react-native';

export default class WebViewScreen extends React.Component {
  render() {
    return (
      <WebView source={require('./pages/demo.html')} />
    )
  }
}

然後在 index.android.js 和 index.ios.js 修改程式碼使用 WebViewScreen 展示

...

import WebViewScreen from './WebViewScreen';

class Demo extends React.Component {
  render() {
    return (
      <View>
        <WebViewScreen />
      </View>
    )
  }
}

AppRegistry.registerComponent('Demo', () => Demo);

添加個 pages/demo.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
  </head>
  <body>
    hello world
  </body>
</html>

然後就可以跑起來測試了,iOS 沒問題,Android 則不會顯示。這是一個已知的問題,目前 0.46 版本還沒有解決。但是我們可以想辦法繞過去,Android 需要先把靜態資源放到 android/app/src/main/assets 目錄下面,然後把 require('./pages/demo.html')換成 {uri: 'file:///android_asset/pages/demo.html'}。WebViewScreen.js 會是下面這個樣子

export default class WebViewScreen extends React.Component {
  const source = (Platform.OS == 'ios') ? require('./pages/demo.html') : { uri: 'file:///android_asset/pages/demo.html' }
  render() {
    return (
      <WebView source={source} />
    )
  }
}

這樣 Android 就沒問題了,但是要記得同步 pages 目錄到 android asset 目錄下。

注意 HTML 中可以引用 javascript 但是不能引用 stylesheet, css 可以使用 <style> 內聯寫在 HTML 裡。

傳資料給 HTML

有時候不只是純靜態的頁面,繼續我們之前圖表的的例子, 圖表的資料是變化的,需要將資料傳給 HTML。 藉助 WebView injectedJavaScript 屬性可以實現從應用傳入資料給 HTML。

改造 pages/demo.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
  </head>
  <body>
    <div id="output"></div>
    <script>
      function init(data) {
        document.getElementById('output').innerHTML = JSON.stringify(data)
      }
    </script>
  </body>
</html>

改造 WebViewScreen.js

export default class WebViewScreen extends React.Component {
  const source = (Platform.OS == 'ios') ? require('./pages/demo.html') : { uri: 'file:///android_asset/pages/demo.html' }
  render() {
    return (
      <WebView source={source} injectedJavaScript={this.bootstrapJS()} />
    )
  }

  bootstrapJS() {
    const data = { hello: 'world' }
    return `init(${JSON.stringify(data)})`
  }
}

injectedJavaScript 可以是一段 JavaScript 程式碼,當頁面載入後注入並執行。