React Native WebView踩坑記

React Native WebView踩坑記

在使用React Native開(kāi)發(fā)應(yīng)用時(shí),有些第三方的頁(yè)面需要在WebView中展示,最初使用WebView展示幾個(gè)簡(jiǎn)單的廣告推廣頁(yè)時(shí),沒(méi)有遇到什么問(wèn)題。隨著使用深入,遇到React Native WebView組件的幾個(gè)問(wèn)題,一番探索,終于明白了問(wèn)題點(diǎn)。

React Native Android WebView body height 100%

遇到這個(gè)問(wèn)題是在使用0.30版本時(shí),在該版本下有一個(gè)外部業(yè)務(wù)的頁(yè)面在我們的WebView展示時(shí)渲染不出來(lái),表現(xiàn)為頁(yè)面是一個(gè)空白頁(yè)。

通過(guò)觀察日志、斷點(diǎn)調(diào)試也沒(méi)有看到什么錯(cuò)誤信息提示。

遇到這種問(wèn)題,思路是首先搜索官方issues,經(jīng)過(guò)搜索,果然發(fā)現(xiàn)已有issue
https://github.com/facebook/react-native/issues/5211

下面的討論中,有人提到了Android端WebView對(duì)height 100%的樣式不能識(shí)別,當(dāng)頁(yè)面body設(shè)置height 100%時(shí),在WebView中會(huì)為頁(yè)面body設(shè)置height 0,因此頁(yè)面渲染為空白頁(yè)。

有了這個(gè)提示,打開(kāi)chrome://inspect開(kāi)始調(diào)試當(dāng)前WebView,經(jīng)過(guò)審查工具查看樣式,發(fā)現(xiàn)確實(shí)這一頁(yè)面為body設(shè)置了height 100%。幸好WebView允許我們向頁(yè)面注入js代碼,于是可以通過(guò)向頁(yè)面注入下列代碼解決

const jsForInjection = `
  var el = document.getElementsByTagName('body')[0];
  el.style.height = '${Dimensions.get('window').height}px';
`

跟進(jìn)React Native release notes發(fā)現(xiàn),在0.32版本,修復(fù)了這一缺陷,鏈接https://github.com/facebook/react-native/commit/1bb1385c7d199a473f76cdec357de2ab4d1d61b6

React Native Android&iOS WebView view height 100%

看到前文提到的官方說(shuō)明,提到已修復(fù)這一缺陷還沒(méi)高興多久。又遇到了另一個(gè)問(wèn)題。在另一個(gè)業(yè)務(wù)的頁(yè)面中,使用了比較復(fù)雜的布局方式,在頁(yè)面內(nèi)容的某一個(gè)子區(qū)域,又使用了height 100%這種布局。

這一次不只是Android了,iOS的WebView也不能正確解析這一樣式,使用height 100%布局的這一個(gè)子區(qū)域渲染為空白區(qū)域。

這里沒(méi)有通用的解決方式了,其他業(yè)務(wù)如果可以去修改自身的樣式布局當(dāng)然是最好。更實(shí)際的解決方案,是發(fā)現(xiàn)一例,解決一例。

以這里遇到的一個(gè)頁(yè)面為例,通過(guò)chrome://inspect定位到具體是哪一個(gè)區(qū)域的問(wèn)題后,通過(guò)注入js,定向的修改這一樣式。

這是一個(gè)沒(méi)有辦法的辦法了。

 var licaiapp = document.getElementsByClassName('hero-product')[0];
  if(licaiapp){
  licaiapp.style.height = '542px';
  }

React Native Android 私有協(xié)議 crash

在網(wǎng)頁(yè)中使用js通過(guò)私有協(xié)議調(diào)起外部App,這種方式對(duì)大家來(lái)說(shuō)應(yīng)該不會(huì)陌生。

在Android 0.30版本的使用中,這里沒(méi)遇到問(wèn)題,但是升級(jí)到0.35版本后,發(fā)現(xiàn)在用戶(hù)沒(méi)有安裝App A時(shí),在WebView加載的頁(yè)面中,通過(guò)私有協(xié)議調(diào)起App A時(shí),會(huì)導(dǎo)致App crash。

通過(guò)查看Android Studio日志,發(fā)現(xiàn)下面異常信息

No Activity found to handle Intent { act=android.intent.action.VIEW dat=investapp://webend-promotion flg=0x10000000 }

根據(jù)這一異常信息,查看安裝端ReactWebViewManager源碼,發(fā)現(xiàn)問(wèn)題的根源在于下面方法

@Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        if (url.startsWith("http://") || url.startsWith("https://")) {
          return false;
        } else {
          Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); 
          intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
          view.getContext().startActivity(intent);   
          return true;   
        }              
    }

React Native 0.31版本開(kāi)始,在ReactWebViewManager中增加了上述方法,加載私有協(xié)議時(shí),通過(guò)else中的邏輯調(diào)起App。問(wèn)題是這里代碼,在用戶(hù)未安裝這一App,startActivity不能被正確響應(yīng)時(shí)會(huì)拋出Exception,而代碼的調(diào)用處又沒(méi)有try/catch,因此導(dǎo)致了crash

給官方提了issue,不過(guò)很快被close說(shuō)覺(jué)得不是RN的問(wèn)題。。。
https://github.com/facebook/react-native/issues/10499

看其他issue也有提到這個(gè)bug,不再去修改問(wèn)題了。

解決辦法,基于官方的ReactWebViewManager做下修改吧,然后封裝一個(gè)自定義WebView出來(lái)給JS端調(diào)用。

React Native iOS 私有協(xié)議問(wèn)題

在iOS模擬器加載一個(gè)外部業(yè)務(wù)的頁(yè)面時(shí),遇到一個(gè)奇怪的問(wèn)題。業(yè)務(wù)的頁(yè)面,首先在WebView里渲染出來(lái),然后又跳去了默認(rèn)失敗界面,如下圖所示

起初一直沒(méi)理解到底是什么問(wèn)題,實(shí)際上已有issue說(shuō)的還算明白了
https://github.com/facebook/react-native/issues/9037

最初在沒(méi)理解問(wèn)題是什么時(shí)候,嘗試了其他方案,引入了社區(qū)的react-native-wkwebview來(lái)替代官方的WebView,引入后發(fā)現(xiàn)確實(shí)解決了問(wèn)題,頁(yè)面可以正常渲染,在已安裝目標(biāo)App的手機(jī)上,也可以調(diào)起App了。

后來(lái)隨著對(duì)Android端WebView問(wèn)題的跟進(jìn),在加上又看了下上面反饋的issue,意識(shí)到這里也是因?yàn)閃ebView加載私有協(xié)議失敗引起的,不同的是,在iOS端,加載私有協(xié)議失敗后,進(jìn)入了失敗處理邏輯,跳到了失敗提示界面。

解決方案,iOS端為解決這一問(wèn)題提供了一個(gè)便利的方法,

onShouldStartLoadWithRequest

可以在這個(gè)方法中,根據(jù)url的協(xié)議頭,決定是否真的發(fā)起請(qǐng)求,還是調(diào)起外部App,代碼如下


onShouldStartLoadWithRequest(event){
        if(event.url.startsWith('http://') || event.url.startsWith('https://')) {
            return true;
        }else{
            Linking.canOpenURL(event.url)
                .then(supported => {
                    if(supported){
                       return Linking.openURL(url);
                    }else{
                        return false;
                    }
                }).catch(err => {
                    return false;
            })
        }
    }

ReactNative與WebView雙向通信

這個(gè)問(wèn)題當(dāng)前業(yè)務(wù)中還沒(méi)用到,不過(guò)目前官方API,僅支持向WebView中注入JS,還不支持從WebView中的頁(yè)面調(diào)用React Native中的方法。

解決辦法,社區(qū)已有一個(gè)解決方案react-native-webview-bridge

如果覺(jué)得有幫助,可以掃描二維碼對(duì)我打賞,謝謝

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容