React Native(三)WebView使用以及雙向通訊

一、各平臺Web控件簡單使用

當使用某個平臺開發(fā)app時候,難免有需要引入網(wǎng)頁的場景,使用方法不外乎引入對應平臺的WebView控件,同時設置好URL地址即可:

iOS平臺:使用UIWebView,WKWebView,并調(diào)用對應的控件方法來實現(xiàn)
- (void)createWebView { 
    // 1.創(chuàng)建控件
    UIWebView *webView = [[UIWebView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    [self.view addSubview:self.webView];
    // 2.請求URL
    NSURL *url = [NSURL URLWithString:@"http://www.github.com"]
    // 3.創(chuàng)建請求
    NSMutableURLRequest *request =[NSMutableURLRequest requestWithURL:url]
    // 4.加載頁面
    [webView loadRequest:request];
}

其中UIWebView是早期的選擇,但由于占用過多的內(nèi)存以及性能上的原因,從IOS8開始用WKWebView來取代笨重的UIWebVie,創(chuàng)建的方式僅僅將UIWebView替換成WKWebView即可

RN平臺:使用RN里的WebView控件,并設置控件的屬性來實現(xiàn)(iOS中總是方法調(diào)用,RN總是屬性設置)
import React, { Component } from 'react';
import { WebView } from 'react-native';

class MyWeb extends Component {
  render() {
    return (
      <WebView
        source={{uri: 'https://github.com'}}
      />
    );
  }
}

我們知道React Native不像Hybrid,控件的實現(xiàn)往往離不開 UIKit 等框架,調(diào)用的也是原生的 Objective-C 代碼。那WebView控件是如何封裝呢?查看任意RN工程下/node_modules/Racct-native/React/Views/RCTWebView.m的源代碼:

@implementation RCTWebView {
  UIWebView *_webView;
  NSString *_injectedJavaScript;
}

可以看出它是基于UIWebView來實現(xiàn)的,它的性能很大程度上就取決于UIWebVIew的性能,猜測不久會替換成WKWebView的實現(xiàn)方式吧。

二、各平臺與Web的通訊

稍微復雜點的應用已經(jīng)不滿足頁面的展示,往往需要平臺和頁面進行通訊,比如想在一個展示用戶信息的Web頁面對用戶進行關注操作,大致有三個環(huán)節(jié):

  • Web調(diào)用平臺的FollowUser接口,同時約定好結果的CallBack
  • 平臺在該接口進行網(wǎng)絡請求并執(zhí)行相應操作
  • 平臺調(diào)用Web頁面的CallBack函數(shù),并把結果作為參數(shù)傳遞回去

而實現(xiàn)的基礎就是各個平臺如何Web進行通訊,對平臺而言,就是要實現(xiàn)監(jiān)聽收到Web消息(也稱作Web調(diào)用平臺),以及如何調(diào)用Web的方法。

iOS平臺WKWebView:
  • 平臺監(jiān)聽Web:平臺通過捕獲Web端JS調(diào)用alert、confirm、prompt的信息來實現(xiàn)
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt 
     defaultText:(nullable NSString *)defaultText 
     initiatedByFrame:(WKFrameInfo *)frame 
     completionHandler:(void (^)(NSString * _Nullable result))completionHandler;

由于已經(jīng)在原生環(huán)境,故可以在該函數(shù)中去執(zhí)行平臺的一些操作,這里有意思的是你通過在completionHandler傳回信息,在Web端可以在alert關閉后獲取到對應的數(shù)據(jù),這樣可以同步方式調(diào)用原生的效果。

  • 平臺調(diào)用Web:
- (void)evaluateJavaScript:(NSString *)javaScriptString       
    completionHandler:(void (^ _Nullable)(_Nullable id, NSError * _Nullable error))completionHandler;

基于上面兩個函數(shù),就能進行一定的封裝,比如Web端約定好調(diào)用的協(xié)議,也就是prompt字符串的如何解析等等,具體業(yè)務場景會在另外一篇中介紹,這里僅截取一段:

//navite調(diào)用Web
//ex:window['NEW_GAME']['onLikeCommentSuccess'](12342)
- (void)callHandler:(NSString *)methodName arg:(NSString*)arg
  completionHandler:(void (^)(NSString *  _Nullable))completionHandler {
    NSString *script = [NSString stringWithFormat:@"window['NEW_GAME']['%@'](%@)" , methodName, arg];
    [self.webView evaluateJavaScript:script completionHandler:^(id value,NSError * error){
        if(completionHandler) completionHandler(value);
    }];
}
iOS平臺UIWebView:
  • 平臺監(jiān)聽Web:js中指定document.location的值,由于webView的重定向原理,OC中的shouldStartLoadWithRequest函數(shù)將會捕獲到處理請求
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request 
    navigationType:(UIWebViewNavigationType)navigationType;
  • 平臺調(diào)用Web:
- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;

WebViewJavascript就是基于此進行封裝,這也是和UIWebView最大的區(qū)別,不過兩種怎么看都有種黑科技的感覺

RN平臺WebView:
  • 平臺監(jiān)聽Web:

首先RN需要設置onMessage屬性來監(jiān)聽Web的消息

import * as React from 'react'
import { WebView } from 'react-native'

class TestWeb extends React.Component {
    webview: WebView
    handleMessage = (evt: any) => {
        const message = evt.nativeEvent.data
    }
    render() {
        return ( <WebView
                ref={webview => this.webview = webview}
                onMessage={this.handleMessage}
            />
        )
    }
}

然后在handleMessage函數(shù)里你就可以統(tǒng)一處理收到的WebView的消息了,這里的onMessage屬性是什么呢?還是RCTWebView.m文件

@property (nonatomic, copy) RCTDirectEventBlock onMessage;
- (BOOL)webView:(__unused UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request
 navigationType:(UIWebViewNavigationType)navigationType{
    BOOL isJSNavigation = [request.URL.scheme isEqualToString:RCTJSNavigationScheme];
    ...
    if (isJSNavigation && [request.URL.host isEqualToString:kPostMessageHost]) {
       [_webView stringByEvaluatingJavaScriptFromString:source];
       _onMessage(event);
    }
}

RN通過判斷請求地址是否用作JS通訊,如果是,則取出event數(shù)據(jù),并調(diào)用用戶綁定的onMessge方法

  • 平臺調(diào)用Web

在WebView的ref屬性執(zhí)行之后的任意地方你就可以調(diào)用postMessage向WebView發(fā)送消息

const message: string = 'RN->Web'     
this.webview.postMessage(message);

這里的postMessage是什么呢?還是RCTWebView.m文件

- (void)postMessage:(NSString *)message {
  NSDictionary *eventInitDict = @{
    @"data": message,
  };
  NSString *source = [NSString
    stringWithFormat:@"document.dispatchEvent(new MessageEvent('message', %@));",
    RCTJSONStringify(eventInitDict, NULL)
  ];
  [_webView stringByEvaluatingJavaScriptFromString:source];
}

可見postMessage實際上是通過調(diào)用UIWebView的stringByEvaluatingJavaScriptFromString函數(shù)來實現(xiàn)
Web端HTML頁面部分

  • Web監(jiān)聽平臺
window.document.addEventListener('message', function (e) {
    const message = e.data
})
  • Web調(diào)用平臺
const message: string = 'Web->RN'
this.webview.postMessage(message)

三、總結:

  • RN中的WebView控件是基本上是對原生控件的封裝,而原生控件蘋果會本身會不斷進行更新,那RN自然也是需要持續(xù)更新來保證最佳的性能
  • 如果需要對RN通訊進行一定程度的封裝,也要謹慎的采用不入侵的方式進行封裝。

四、備注:

  • onMessage/postMessage是RN從 0.37 版才支持的。支持android和iOS,以上代碼僅提供iOS,android原理差不多。

附上項目Demo地址
本人初學RN,歡迎留言指正,也歡迎關注收藏

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,030評論 25 709
  • 發(fā)現(xiàn) 關注 消息 iOS 第三方庫、插件、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 15,293評論 4 61
  • 前言 關于UIWebView的介紹,相信看過上文的小伙伴們,已經(jīng)大概清楚了吧,如果有問題,歡迎提問。 本文是本系列...
    CoderLF閱讀 9,340評論 2 12
  • Vue v2.0 自定義指令 顧名思義,這個是為vue移動端web項目打造的觸摸反饋指令,高度模擬App反饋效果;...
    昨天之前閱讀 2,020評論 0 3
  • 有時候真的有種想放棄的感覺。但是,又不知道自己放棄以后,真的會變得比現(xiàn)在好嗎?或者放棄以后,有能力找到更好的嗎...
    Syusuke閱讀 531評論 1 2

友情鏈接更多精彩內(nèi)容