最近在使用react-native的時候遇到了很多坑,這里給大家分享下
一.樣式
react-native 雖然支持flex布局,但是所有的樣式均是css樣式的一個很小的集合,尤其是在安卓機下問題尤為凸顯:
1.View內部的元素千萬不要超出父級的范圍,iso上問題倒是不大,安卓上就什么超出的都看不到了
2.lineHeight 可以用,不過千萬不要寫成小數,否則安卓上會直接崩潰
3.rn的樣式不存在繼承的情況,所以基本上每個節點都要寫style,真的是體力活
4.如果Text的父級元素設置了背景顏色,那么ios下Text的背景顏色也是父級的背景顏色,要么自己寫個Text重置下樣式,要么就遇到了再改
5.react-native的字號是沒有設置單位的,所以會隨着系統設置的字體大小而變化,我也不知道這是不是坑,不過貌似有的app也沒有管這個,如果硬要去設置Text的文字不隨系統改變,安卓是可以統一設置的,ios上Text設置allowFontScaling ={false}就可以解決
二.異常
react-native 在發生js異常的時候,debug的時候會直接紅屏幕,但是再release的時候直接會崩潰退出,解決辦法
import ErrorUtils from "ErrorUtils"
//這里應該做個判斷,如果不是debug的才做這樣的異常全局處理 ErrorUtils.setGlobalHandler((e)=>{
//發生異常的處理方法,當然如果是打包好的話可能你找都找不到是哪段代碼出問題了 Alert.alert("異常",JSON.stringify(e)) });
三.fetch
react-native雖然自帶有fetch,不過在使用的時候發現了一個問題,如果需要獲取http的header頭的時候問題就來了,可能得到的是一些千奇百怪的樣式,這並不是react-native的錯,而是第三方的 whatwg-fetch 留下的坑,當然也有人再github上跟react-native反映過這個問題,不過得到的解決方案都很坑,唯有一個辦法,就是拷貝自己修改,修改如下:
1.注釋該注釋的
(function(self) { 'use strict'; //注釋這里,不然總是用的是全局的fetch // if (self.fetch) { // return // }
2.修改該修改的
function parseHeaders(rawHeaders) { var headers = new Headers()
//把\t\n改成\t,因為一般header都是用\n來分割的 rawHeaders.split('\n').forEach(function(line) { //rawHeaders.split('\t\n').forEach(function(line) { var parts = line.split(':') var key = parts.shift().trim() if (key) { var value = parts.join(':').trim() headers.append(key, value) } }) } return headers }
3.直接import你改好的文件,fetch就可以用了
四.Modal
Mode控件在使用的時候要注意了,因為這個是rn提供的,並且也寫的很清楚是最高層級的一個彈出層,所以你想要又打開Model又要跳轉基本是無望的了,所以建議不要使用這個,最好是使用第三方的控件,我們用的是 react-native-modalbox + 高階控件 實現的全遮蓋的彈出層
五.點擊屏幕其他位置關閉的菜單
這類菜單有個共同的特點就是點擊屏幕其他地方然后菜單就關閉,我們的解決辦法就是用自己寫的 react-native-modalbox + 高階控件 也就是說放在一個彈出層里面,當然可以試試把當前頁面套進一個大的 TouchableWithoutFeedback 里面
六.接口請求
非特殊情況下都應該這樣做
import {InteractionManager} from "react-native" componentDidMount(){ InteractionManager.runAfterInteractions(() => { fetch("xxx.xxx.xxx",{}) }); }
七.鍵盤
官方提供的自定義隱藏鍵盤的方法是
import { Keyboard } from 'react-native' Keyboard.dismiss()
但是我試了很多次之后發現根本不能,而且還報錯,樓主的react-native版本是0.35.0
看了官方的issue才知道這個不行,推薦下面方法
import dismissKeyboard from 'dismissKeyboard'
dismissKeyboard()
這樣就可以隱藏了,太坑了
還有個很坑的地方,官方提供的移除鍵盤事件的方法不可用
componentDidMount () {
Keyboard.addListener('keyboardDidShow', this.keyboardDidShow.bind(this))
Keyboard.addListener('keyboardDidHide', this.keyboardDidHide.bind(this))
}
componentWillUnmount () { Keyboard.removeAllListeners('keyboardDidShow') Keyboard.removeAllListeners('keyboardDidHide') }
這樣的方式特么的如果操作快了,或者有時候莫名其妙的就會出錯,下面的才是正確的打開方式:
componentDidMount () { this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this.keyboardDidShow.bind(this)) this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this.keyboardDidHide.bind(this)) } componentWillUnmount () { this.keyboardDidShowListener.remove() this.keyboardDidHideListener.remove() }
八.https
https這個問題上ios還好,安卓問題就來了,前期我們准備將ajax請求的庫丟給原生安卓和ios來做我們直接調用就是了,但是后來發現問題這樣那樣的問題太多了,
所以在熱更新服務器啟動或者打包的時候就把源代碼先改了在進行打包或者啟動服務器
文件位置:
node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/network/OkHttpClientProvider.java
這個文件的最后一個方法修改如下:
private static OkHttpClient createClient() { // No timeouts by default return new OkHttpClient.Builder() .sslSocketFactory(sslContext.getSocketFactory()) .hostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String hostname, SSLSession session) { return true; //忽略所有的認證,直接返回了true } }) .connectTimeout(0, TimeUnit.MILLISECONDS) .readTimeout(0, TimeUnit.MILLISECONDS) .writeTimeout(0, TimeUnit.MILLISECONDS) .cookieJar(new ReactCookieJarContainer()) .build(); }
修改源代碼的方式有點略坑,不過可以解決很多問題,還節約時間!!!
九.BackAndroid
安卓機有獨特的點擊按鍵返回,所以在最外層會注冊一個監聽方法
bindHardwareBackPress(){ if (Platform.OS === 'android') { BackAndroid.addEventListener('hardwareBackPress', this._onHomeBackPress); } } onHomeBackPress(){ let routeList = this.getRouteList(); if (routeList.length !== 1) { this.navigator.pop(); return true; } this.handleHomeBackPress(); return true; } handleHomeBackPress(){ if (Platform.OS === "android") { ToastAndroid.show("再按一次退出應用", ToastAndroid.SHORT); BackAndroid.removeEventListener("hardwareBackPress", this._onHomeBackPress); BackAndroid.addEventListener("hardwareBackPress", this._onExitApp); this.timer = TimerMixin.setInterval(() => { TimerMixin.clearInterval(this.timer); BackAndroid.removeEventListener("hardwareBackPress", this._onExitApp); BackAndroid.addEventListener("hardwareBackPress", this._onHomeBackPress); }, 2000); } } exitApp(){ BackAndroid.exitApp(); }
上面的代碼是監聽返回鍵,如果不是在最外層的路由就返回上一個,如果在最外層就直接關閉app,但是有很多這樣那樣的需求要去對安卓的返回鍵進行操作,坑就來了,你以為提供的removeEventListener方法是沒問題的?no !!! 他會移除所有的監聽,這是不是很坑!!!!
所以:在需要對安卓返回鍵進行特殊處理的時候記得其他地方做了監聽的再重新監聽一次!!!!