react-native中IOS的webview和js層通訊 - UIWebview

前言

在9012的最後一篇寫到了在rn中安卓的webview的通訊原理,而做爲0202年的第一篇,繼續討論上年rn中webview通訊剩下的部分。react

背景:

對於webview,瞭解過的人都知道在ios端會存在兩種類型的webview(UIWebview和WKWebview),而他們之間的區別主要以下:ios

  • 時間上的差別:UIwebview是在ios2以後纔有,Wkwebview是在ios8以後纔有;
  • UIwebview佔用的內存較大,而WKwebview側是內存佔有很少,所以性能會比前者好;
  • WKwebview擁有高達60FPS滾動刷新率(滾動時每秒可達到60次渲染,通常最多64次)及內置手勢;
  • WKwebview比UIwebview擁有更多H5的特性;
  • WKwebview提供了加載網頁的進度屬性;
  • WKwebview在app和web通訊方面比UIwebview要好;
  • WKWebView沒有作緩存處理,因此對網頁須要緩存的加載性能要求沒那麼高的仍是能夠考慮UIWebView.

RN中IOS的webview結構:

大體流程:

在ios中,應用層會設置rn中webview的某個屬性,而weview會經過webview.iso.js進行處理,若是該屬性是原生底層直接擁有的,那麼就直接往下傳遞,若是該屬性不是底層所擁有的,則須要經過在webview.ios.js層處理成底層組件所擁有的屬性,而後再往下傳遞下去,由RCTWebview或者RCTWKWebview進行處理。web

例如:
source屬性:webview層設置了source -----> webview.ios.js往下傳遞 ------> RCTWebviewManager.m能接受到該屬性,而後繼續往下傳遞 -----> RCTWebview進行處理該屬性;segmentfault

onLoadStart屬性:webview層設置了onLoadStart ----> webview.ios.js對該屬性進行處理,包裝成_onLoadingStart,而後傳給RCTWebviewManager ----> RCTWebviewManager.m能接受到該屬性,而後繼續往下傳遞 -----> RCTWebview進行處理該屬性;瀏覽器

大概的流程圖:

image.png

UIWebview的相關知識:

ios和js的交互
  • stringEvaluatingJavascriptFromString:該方法時可讓ios層執行js代碼;

語法形式:緩存

- (void)injectJavaScript:(NSString *)script
{
  [_webView stringByEvaluatingJavaScriptFromString:script];
}

當js層傳遞js字符串給ios的時候,ios就會用stringEvaluatingJavascriptFromString這個方法執行這個script。app

  • shouldStartLoadWithRequest: 該方法是用來對webview中的url進行攔截,而後判斷是否打開這個url連接,須要返回一個boolean值。當webview中存在一個url連接,就會觸發這個方法,而後對url進行判斷,若是是返回boolean,則能夠打開url的連接,不然不行。

image.png

生命週期

對於uiwebview來講,通常有三個生命週期他們分別是:post

  • shouldStartLoadWithRequest:頁面在準備加載資源;
  • webViewDidFinishLoad:頁面資源完成加載完畢;
  • didFailLoadWithError:資源加載失敗;

rn中UIWebview和js之間的通訊機制:

在webview之中通訊最爲重要,經過postMessage向rn層傳遞信息,rn層只須要message就行,相反也行。該實現的邏輯是在資源加載完成的時候執行的,就是在webviewDidFinishLoad
image.png性能

內嵌頁面向rn傳遞信息和接受信息:ui

window.postMessage(data); 
document.addEventListener('message', (event) => {});

rn層向內嵌頁面傳遞信息:

<Webview
   onMessage={event => {}}
   ref={(_ref) => {this.webview = _ref}}
   ...
/>
this.webview.postMessage(data)

細心的朋友此處應該能夠發現出這裏的postMessage和平時咱們調用的不太同樣:
通常狀況下:window.postMessage(data, origin);
內嵌在webview之中:window.postMessage(data);
緣由是由於在uiwebview之中從新定義了window.postMessage,原來的postmessage已經被賦予到originMessage之中,因此若是在內嵌頁面中出現了postmessage(data),放在瀏覽器會報錯,放在rn的webview就會沒事。

當頁面資源加載完成以後,就會觸發webviewDidFinishLoad這個生命週期,在這個生命週期內,會作幾件事情:

image.png

其中從新定義postmessage的js代碼:

image.png

大概的意思就是將內嵌頁面調用postmessage的信息存在messagequeue之中(以字符串形式放進去),而後經過定義window.location的值來傳遞信息給原生成,其中window.location的格式是:
'%@://%@?' + encodeURIComponent(messageQueue.shift());" ,當中的兩個%@會被RCTJSNavigationScheme和kPostMessageHost代替從而造成新的url地址(不是經常使用的http協議的地址,是指定的特殊協議)

例如:'react-js-navigation://postMessage?' + encodeURIComponent('{"A":1}') ===> "react-js-navigation://postMessage?%7B%22A%22%3A1%7D";因爲內嵌頁面沒有window,因此只能用document來監聽webview傳來的信息。

postmessage的信息如何傳遞給原生層:每當webview之中出現url的請求,都會觸發shouldStartLoadWithRequest,此時就會由

image.png

大概就是判斷url的host是否爲kPostMessageHost,若是是則認爲是postmessage傳來的,而後執行_onMessage方法,而這個方法事由上層js定義的:
image.png

webview如何將信息傳遞給內嵌頁面:
image.png

大概就是經過document.dispatchEvent發起一個事件,而後在內嵌頁面之中由document進行監聽;

下面就是整一塊uiwebview的通訊流程:
image.png

因此若是要js層調用原生層能力,能夠經過協議請求,而後在原生對該協議進行攔截處理,從而判斷是否調用原生能力。

以上就是對uiwebview的一個比較淺的概述,下一篇會繼續介紹一下rn中wkwebview的通訊原理。

相關文章
相關標籤/搜索