iT邦幫忙

2023 iThome 鐵人賽

DAY 15
0
Mobile Development

30天React Native之旅:從入門到活用系列 第 15

Day 15:使用WebView在React Native中展示網頁

  • 分享至 

  • xImage
  •  

WebView組件可以透過url展示一個網頁,或是傳入自訂的html顯示。

安裝 react-native-webview

由於React Native不再內建WebView功能,已交給社群維護。所以首先我們需要進行安裝:

npm install --save react-native-webview
cd ios && pod install

基本使用

  • 載入網址:WebView的基本使用方式。很簡單,就是把網址放到source屬性

    import React, { Component } from 'react';
    import { WebView } from 'react-native-webview';
    
    class MyWeb extends Component {
      render() {
        return <WebView source={{ uri: 'https://reactnative.dev/' }} />;
      }
    }
    
  • 插入HTML:
    除了放網址,也支援直接渲染HTML。

    import React, { Component } from 'react';
    import { StyleSheet, View } from 'react-native';
    import { WebView } from 'react-native-webview';
    
    const MyHTMLComponent = () => {
      const htmlContent = `
        <html>
          <head></head>
          <body>
            <h1>Welcome to React Native WebView!</h1>
            <p>This is rendered using local HTML content.</p>
          </body>
        </html>
      `;
    
      return (
        <View style={{ flex: 1 }}>
          <WebView 
            source={{ html: htmlContent }} 
          />
        </View>
      );
    }
    
    export default MyHTMLComponent;
    

React Native 與 WebView 間的資料傳遞

在某些情境,我們可能希望向 WebView 傳資料,例如傳Token,以讓 WebView 裡的網頁保持登入狀態。要注意的是,只能透過字串來傳遞。
因此,開發時APP開發和Web開發雙方需做好溝通,協議好資料格式。例如使用JSON格式來序列化和解析數據。

  • WebView發送資料給React Native
    • Web端:使用 window.ReactNativeWebView.postMessage
      // WebView 中的Web
      function sendDataToReactNative(data) {
          window.ReactNativeWebView.postMessage(data);
      }
      
    • React Native端:使用onMessage屬性來捕獲從WebView發送過來的消息。
      import { WebView } from 'react-native-webview';
      
      function App() {
          const onReceiveMessage = (event) => {
              const data = event.nativeEvent.data;
              console.log("Received data from WebView:", data);
          };
      
          return (
              <WebView
                  source={{ uri: 'https://your-webview-url.com' }}
                  onMessage={onReceiveMessage}
              />
          );
      }
      
  • React Native發送資料給WebView
    • React Native端:使用WebView的postMessage方法。

      const webViewRef = useRef(null);
      
      function sendDataToWebView(data) {
          webViewRef.current.postMessage(data);
      }
      
      return (
          <WebView
              ref={webViewRef}
              source={{ uri: 'https://your-webview-url.com' }}
              onMessage={onReceiveMessage}
          />
      );
      
    • Web端:監聽message事件來捕獲從React Native發送過來的消息。

      window.addEventListener('message', function(event) {
        const dataFromReactNative = event.data;
        console.log("Received data from React Native:", dataFromReactNative);
      });
      

調試WebView中的網頁

當WebView中的網頁出現問題時,我們會需要調試WebView中的網頁。記得筆者以前剛接觸React Native時,曾經有一次遇到了WebView的問題。由於當時不知道如何調試WebView,結果耗費了好幾個小時解決問題。

  • IOS調試

    1. 先開啟Safari → 前往「設定」→「進階」→ 啟用”在選單列中顯示開發選單”。
      https://ithelp.ithome.com.tw/upload/images/20230930/20103365JdbhUEMcyK.png
    2. 為組件加上webviewDebuggingEnabled={true}以啟用調試模式。
      <WebView 
          source={require('./src/simple.html')} 
          webviewDebuggingEnabled={true}
      />
      
    3. Safari,選擇「開發」→ 選擇目前運行的模擬器。若 WebView 正在運行,你將會看到相對應的網頁可供選擇。
      https://ithelp.ithome.com.tw/upload/images/20230930/20103365EcFbx9Ef0F.png
    4. 選擇該網頁,調試工具就會出現。
      https://ithelp.ithome.com.tw/upload/images/20230930/20103365HND44cPF3Y.png
    • 若第三步沒出現對應的網頁,可能是ios版本問題,可以手動到node_modules/react-native-webview/apple/RNCWebView.m,添加_webView.inspectable = YES,使 WebView 可被檢查;
      _webView = [[WKWebView alloc] initWithFrame:self.bounds configuration: wkWebViewConfig];
      _webView.inspectable = YES; // 增加這一行
      
  • Android調試

    1. 開啟Chrome瀏覽器,於網址列中輸入 chrome://inspect/#devices 並按下Enter。
    2. 在「Devices」,找到你要調試的網頁。
      https://ithelp.ithome.com.tw/upload/images/20230930/20103365xuhXeA8Slx.png
    3. 點選「inspect」,調試工具就會出現。

實戰

我們做一個WebView,加載中要顯示Loading動畫,頁面加載完畢時再顯示內容。同時,還要傳送Token資料到WebView。

  1. 創建WebView

    <View style={styles.container}>
       <WebView source={require('./simple.html')} />
    </View>
    
  2. 引入加載動畫ActivityIndicator,並用useState設定Loading狀態,初始設定為true。
    然後在<WebView />增加onLoadEnd屬性,這屬性是用來設定加載完畢的回調,我們將回調後Loading狀態改為false

    import { ActivityIndicator } from 'react-native';
    
    const WebExample = () => {
      const [isLoading, setLoading] = useState(true);
    
      return (
        <View style={styles.container}>
                {isLoading && (
            <View style={styles.overlay}>
              <ActivityIndicator size="large" color="#0000ff" />
            </View>
          )}
          <WebView 
            source={require('./simple.html')}
            onLoadEnd={() => setLoading(false)} 
          />
        </View>
      );
    };
    
  3. WebView設置useRef,並在加載完畢時執行postMessage傳送Token

     const WebExample = () => {
       const [isLoading, setLoading] = useState(true);
       const refWebView = useRef(null)
    
       const TOKEN = "123";
    
       return (
         <View style={styles.container}>
           {isLoading && <ActivityIndicator style={styles.loader} size="large" color="#0000ff" />}
           <WebView 
             source={require('./simple.html')}
             onLoadEnd={() => {
               setLoading(false);
               refWebView.current?.postMessage(TOKEN);
             }}
           />
         </View>
       );
     };
    
  4. 處理Web端的接收,用一個彈窗測試是否接收到Token

    window.addEventListener("message", function(event) {
        const tokenFromReactNative = event.data;
        alert("Received token: " + tokenFromReactNative);
    });
    

    完成!
    gexb8C7

    完整代碼

     import React, { useState, useRef} from 'react';
     import { StyleSheet, View, ActivityIndicator } from 'react-native';
     import { WebView } from 'react-native-webview';
    
     const WebExample = () => {
       const [isLoading, setLoading] = useState(true);
       const refWebView = useRef(null)
    
       const TOKEN = "123";
    
       return (
         <View style={styles.container}>
           {isLoading && (
             <View style={styles.overlay}>
               <ActivityIndicator size="large" color="#0000ff" />
             </View>
           )}
           <WebView 
             ref={refWebView}        
             source={require('./simple.html')} 
             style={{ flex: 1 }}
             onLoadEnd={() => {
               setLoading(false);
               refWebView.current?.postMessage(TOKEN);
             }}
           />
         </View>
       );
     };
    
     const styles = StyleSheet.create({
       container: {
         flex: 1,
         position: 'relative', 
       },
       overlay: {
         ...StyleSheet.absoluteFillObject,
         justifyContent: 'center', 
         alignItems: 'center',  
         backgroundColor: 'rgba(255,255,255,0.9)'
       }
     });
    
     export default WebExample;
    

    simple.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Sample Page</title>
        <style>
        </style>
    </head>
    <body>
        <script>
            window.addEventListener("message", function(event) {
                const tokenFromReactNative = event.data;
                alert("Received token: " + tokenFromReactNative);
            });
        </script>
    </body>
    </html>
    

上一篇
Day 14:高效React Native動畫:探索Reanimated
下一篇
Day 16:打造用戶體驗良好的TextInput
系列文
30天React Native之旅:從入門到活用30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言