React Native WebView踩坑記
在使用React Native開發(fā)應用時,有些第三方的頁面需要在WebView中展示硕舆,最初使用WebView展示幾個簡單的廣告推廣頁時,沒有遇到什么問題骤公。隨著使用深入,遇到React Native WebView組件的幾個問題阶捆,一番探索,終于明白了問題點刊咳。
React Native Android WebView body height 100%
遇到這個問題是在使用0.30版本時儡司,在該版本下有一個外部業(yè)務的頁面在我們的WebView展示時渲染不出來,表現為頁面是一個空白頁捕犬。
通過觀察日志、斷點調試也沒有看到什么錯誤信息提示碉碉。
遇到這種問題,思路是首先搜索官方issues,經過搜索贴届,果然發(fā)現已有issue
https://github.com/facebook/react-native/issues/5211
下面的討論中,有人提到了Android端WebView對height 100%的樣式不能識別毫蚓,當頁面body設置height 100%時昔善,在WebView中會為頁面body設置height 0,因此頁面渲染為空白頁君仆。
有了這個提示牲距,打開chrome://inspect開始調試當前WebView钥庇,經過審查工具查看樣式,發(fā)現確實這一頁面為body設置了height 100%皮服。幸好WebView允許我們向頁面注入js代碼参咙,于是可以通過向頁面注入下列代碼解決
const jsForInjection = `
var el = document.getElementsByTagName('body')[0];
el.style.height = '${Dimensions.get('window').height}px';
`
跟進React Native release notes發(fā)現,在0.32版本蕴侧,修復了這一缺陷,鏈接https://github.com/facebook/react-native/commit/1bb1385c7d199a473f76cdec357de2ab4d1d61b6
React Native Android&iOS WebView view height 100%
看到前文提到的官方說明敲才,提到已修復這一缺陷還沒高興多久。又遇到了另一個問題紧武。在另一個業(yè)務的頁面中,使用了比較復雜的布局方式敏储,在頁面內容的某一個子區(qū)域阻星,又使用了height 100%這種布局已添。
這一次不只是Android了,iOS的WebView也不能正確解析這一樣式畦幢,使用height 100%布局的這一個子區(qū)域渲染為空白區(qū)域缆蝉。
這里沒有通用的解決方式了,其他業(yè)務如果可以去修改自身的樣式布局當然是最好刊头。更實際的解決方案,是發(fā)現一例,解決一例弦讽。
以這里遇到的一個頁面為例膀哲,通過chrome://inspect定位到具體是哪一個區(qū)域的問題后被碗,通過注入js,定向的修改這一樣式兴喂。
這是一個沒有辦法的辦法了焚志。
var licaiapp = document.getElementsByClassName('hero-product')[0];
if(licaiapp){
licaiapp.style.height = '542px';
}
React Native Android 私有協議 crash
在網頁中使用js通過私有協議調起外部App,這種方式對大家來說應該不會陌生酱酬。
在Android 0.30版本的使用中,這里沒遇到問題汗菜,但是升級到0.35版本后挑社,發(fā)現在用戶沒有安裝App A時,在WebView加載的頁面中痛阻,通過私有協議調起App A時,會導致App crash麻车。
通過查看Android Studio日志斗这,發(fā)現下面異常信息
No Activity found to handle Intent { act=android.intent.action.VIEW dat=investapp://webend-promotion flg=0x10000000 }
根據這一異常信息,查看安裝端ReactWebViewManager源碼表箭,發(fā)現問題的根源在于下面方法
@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版本開始免钻,在ReactWebViewManager中增加了上述方法极舔,加載私有協議時,通過else中的邏輯調起App拆魏。問題是這里代碼慈俯,在用戶未安裝這一App拥峦,startActivity不能被正確響應時會拋出Exception,而代碼的調用處又沒有try/catch刑峡,因此導致了crash
給官方提了issue玄柠,不過很快被close說覺得不是RN的問題。随闪。。
https://github.com/facebook/react-native/issues/10499
看其他issue也有提到這個bug撮奏,不再去修改問題了当宴。
解決辦法,基于官方的ReactWebViewManager做下修改吧户矢,然后封裝一個自定義WebView出來給JS端調用。
React Native iOS 私有協議問題
在iOS模擬器加載一個外部業(yè)務的頁面時捌年,遇到一個奇怪的問題挂洛。業(yè)務的頁面,首先在WebView里渲染出來虏劲,然后又跳去了默認失敗界面,如下圖所示
起初一直沒理解到底是什么問題励堡,實際上已有issue說的還算明白了
https://github.com/facebook/react-native/issues/9037
最初在沒理解問題是什么時候堡掏,嘗試了其他方案,引入了社區(qū)的react-native-wkwebview來替代官方的WebView摊趾,引入后發(fā)現確實解決了問題,頁面可以正常渲染砾层,在已安裝目標App的手機上贱案,也可以調起App了。
后來隨著對Android端WebView問題的跟進侨糟,在加上又看了下上面反饋的issue瘩燥,意識到這里也是因為WebView加載私有協議失敗引起的,不同的是厉膀,在iOS端,加載私有協議失敗后凳兵,進入了失敗處理邏輯企软,跳到了失敗提示界面。
解決方案仗哨,iOS端為解決這一問題提供了一個便利的方法,
onShouldStartLoadWithRequest
可以在這個方法中萨醒,根據url的協議頭桩卵,決定是否真的發(fā)起請求,還是調起外部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雙向通信
這個問題當前業(yè)務中還沒用到,不過目前官方API辞州,僅支持向WebView中注入JS寥粹,還不支持從WebView中的頁面調用React Native中的方法埃元。
解決辦法媚狰,社區(qū)已有一個解決方案react-native-webview-bridge
如果覺得有幫助,可以掃描二維碼對我打賞崭孤,謝謝