根據(jù)我的項目需要,其實我需要一個很簡單的APP應用,但就是這樣一個簡單的APP應用,我卻耗費了大量時間和精力,因為,坑實在太多了,我就把我遇到的坑寫出來做個記錄。
二、APP開發(fā)
我項目需要以下的頁面
一、主界面
二、登錄
三、個人中心
四、list
五、detail
由于APP與web不一樣,所以沒有URL的概念,相對應的是導航器,初入RN,也沒有使用原生的navigator組件,而是經(jīng)大神推薦查看了ant-design的組件Demo,依葫蘆畫瓢使用了 navigation組件。
1)Navigation組件
首先我們需要安裝 react-native-navigation
npm i -g react-navigation --save
注意,在ios下,所有安裝的npm包,都要yarn一下,主要是依賴的問題,否則會提示 安裝成功,但依賴失敗錯誤
命令:
yarn add react-native-navigation
安裝后,我們就可以使用了
navigation有很豐富的功能,具體可以到?https://reactnavigation.org/docs/navigators/?查看文檔
而我項目只需要使用到?StackNavigator 而沒有TabNavigator/DrawerNavigator導航,所以我只介紹?StackNavigator 和導航切換的方法
引入navigation組件
import { StackNavigator,TabNavigator,NavigationActions } from 'react-navigation';
配置導航
import Login from './Login' // 其他場景
const FirstApp = StackNavigator({?
?????????????????IndexScreen: {?
?????????????????????????screen: IndexScreen, // initialRouteName: 'IndexScreen' navigationOptions:({ navigation })=>({
????????????????????????????????????//headerTitle: '導航標題',headerLeft: () 可以設置 headerTitle headerLeft headerRight等,具體參考官方文檔
? ? ? ? ? ? ? ? ? ? ? ? ?})
? ? ? ? ? ? ? ? },
????????????????// 其他場景: {screen: 場景名稱},
????????????????Login:{screen: Login},
? ? ? ? ? ? ? ? ......
? ? ? ? },
????????{
? ? ? ? ? ? ? ? // ....這里是其他公共配置
? ? ? ? ? ? ? ? // 例如?initialRouteName?navigationOptions等,具體參考官方文檔或網(wǎng)上的navigation的配置資料
????????});
在Component中也可配置navigationOptions
static navigationOptions = ({navigation, screenProps}) => ({
????????headerTitle: '導航標題'
});
請注意,navigationOptions的配置優(yōu)先級為:
StackNavigator中的navigationOptions <?Component中的navigationOptions <?StackNavigator中的Screen的配置
會按優(yōu)先級進行覆蓋
然后是將組件加入APP的component
在registerComponent的component中
render() {?
?????????????return (
????????????????<FirstApp style={{backgroundColor:'#ffffff'}} ref={nav => { this.navigator = nav; }} />
????????????)
}
至此,navigation就配置完成,那么對應的支持的方法分為幾種
第一種,是常用的在子組件中調用
可以通過
this.props.navigation.navigate('Screen Name',{...params});?
進行跳轉或
this.props.navigation.goBack();
進行返回
第二種,在StackNavigator中的navigationOptions中使用
navigationOptions: ({navigation})=>({
? ? ? ? //...在headerLeft等組件的onPress方法中 ()=>{
? ? ? ? //? ? ? navigation.goBack();
? ? ? ? // 或
? ? ? ? //? ? ??navigation.navigate('Screen Name',{...params});
? ? ? ? // }
? ? ? ? // 請注意,在這里需要用到 TouchableOpacity 組件包括按鈕,touchablehighlight會報錯,請注意
});
還有一個特別值得注意的是,當你只有headerRight時,需要設定headerLeft為一個空的view,否則,會導致標題無法居中
headerLeft: (
? ? ? ? <View style={{height: 44,width: 55,paddingRight:15} }/>
)
第三種,在registerComponent 中調用導航方法,這里主要是我使用了極光PUSH所以會在監(jiān)聽時調用導航器,通過控制臺打印this對象發(fā)現(xiàn)了這個方法
this.navigator._navigation.navigate('Screen Name',{...params});
或
this.navigator._navigation.goBack();
至此,Navigation教程完成,相信你能配置一個符合你的導航器
2) 登錄權限驗證
然后就是User登錄判斷,我的需求是需要用戶登錄,那我APP就需要判斷,而又不應該使用cookie來進行處理,查閱了資料后,發(fā)現(xiàn)用戶登錄狀態(tài)判斷代碼,但有錯誤,會導致bundle報錯,所以我對此進行了修正
首先我們需要一個本地儲存引擎,我這里使用的是react-native-storage
用上面的npm方法進行安裝
npm i -g react-native-storage --save
yarn add?react-native-storage
資料上說官方并不推薦直接使用storage,所以我按教程封裝了一個storage方法放在storageUtil.js
var storage = new Storage({
????// 最大容量,默認值1000條數(shù)據(jù)循環(huán)存儲
????size: 1000,
????// 存儲引擎:對于RN使用AsyncStorage,對于web使用window.localStorage
????// 如果不指定則數(shù)據(jù)只會保存在內存中,重啟后即丟失
????storageBackend: AsyncStorage,
????// 數(shù)據(jù)過期時間,默認一整天(1000 * 3600 * 24 毫秒),設為null則永不過期
????defaultExpires: null,
????// 讀寫時在內存中緩存數(shù)據(jù)。默認啟用。
????enableCache: true,
// 如果storage中沒有相應數(shù)據(jù),或數(shù)據(jù)已過期,
// 則會調用相應的sync方法,無縫返回最新數(shù)據(jù)。
// sync方法的具體說明會在后文提到
// 你可以在構造函數(shù)這里就寫好sync的方法
// 或是寫到另一個文件里,這里require引入
// 或是在任何時候,直接對storage.sync進行賦值修改
//sync: require('./sync')? // 這個sync文件是要你自己寫的
})
// 然后設置全局對象,由于我是APP所以不存在web 輕應用,所以不需要用window.storage = storage;而是global.storage = storage;
global.storage = storage;
設置好后,我需要一個Global.js來判斷當用戶打開APP時是否登錄
//用戶登錄數(shù)據(jù)
global.user = {
????loginState:'',//登錄狀態(tài)
????userData:'',//用戶數(shù)據(jù)
};
//刷新的時候重新獲得用戶數(shù)據(jù)
storage.load({
????key: 'loginState',
}).then(ret => {
????global.user.loginState = true;
????global.user.userData = ret;
}).catch(err => {
????global.user.loginState = false;
????global.user.userData = '';
})
然后在registerComponent中import這兩個包,就完成了用戶的驗證
注意:但實際情況卻是,storage.load安卓下并未觸發(fā),我并不清楚這是什么情況引起的,但IOS下又觸發(fā)了,并且調試并未出現(xiàn)錯誤,所以我大膽猜測是import的時候,并未初始化完成storage,導致load方法并未實際執(zhí)行,可能這涉及到RN本身的機制,所以我又想了一個辦法,我封裝了一個SUser.js的全局方法,在每個組件的componentWillMount中進行驗證,當global.user.loginState==false時我就執(zhí)行清除用戶數(shù)據(jù)并返回登錄界面
import JPushModule from 'jpush-react-native';
var Suser = {
????phone:null,
????checklogin:function(t){
????if (!global.user.loginState) {
????????storage.load({
????????????key: 'loginState'
????????}).then(ret => {
????????????global.user.loginState = true;
????????????global.user.userData = ret;
????????????global.user.phone = ret.phone;
????????}).catch(err => {
????????????JPushModule.clearAllNotifications();
????????????global.user.loginState = false;
????????????global.user.userData = '';
????????????t.navigate('Login');
????????})
????}
},
logout:function(t){
????storage.remove({
????????key: 'loginState'
????});
????global.user.loginState = false;
????global.user.userData = '';
????JPushModule.clearAllNotifications();
????t.navigate('Login');
}
}
global.Suser = Suser;
這樣,就解決了用戶登錄判斷,雖然方法很笨,但有效,如有大神有更好的方法,請聯(lián)系我,感激不盡?。?!
還有注意:當你在chorme中查看console時,發(fā)現(xiàn)app init了2次或更多,導致所有事件都會執(zhí)行兩次,例如alert,那么只是模擬器問題,關閉APP,重新啟動就好了,原因我不清楚,偶爾會出現(xiàn)重復2次或3次或4次,調試了很久也沒解決,正式包不存在這個問題,問了大神也不清楚,代碼并未有問題。