React-Native開發總結-react-navigation應用與實踐
最近更新時間:2017年10月29日14:03:20
做開發的同學都知道,難的不是技術本身,而是產品需求的頻繁變更和邏輯複雜度,這讓研發工程師最為苦惱。但總體上來說,積累技術經驗,善於總結和記錄技術的實踐心得,也是一件優雅和愉悅的事情。
對於開發的同學來說,RN開發原生APP的一項技術挑戰是,靈活自如的使用導航器跳轉頁面,對於常規需求的路由棧,入棧需求A>B>C>D + 出棧需求D>C>B>A,這個比較簡單,容易實現,但是真正的專案需求,一般沒有這麼簡單。比如:入棧需求是A>B>C>D,但出棧需求是D>A;入棧需求A>B>C>D ,出棧需求D>B>A,同時重新整理B(常規返回上級路由,不會重新整理上級頁面);因此,需要細心設計。
1、react-navigation官方文件
請檢視官方文件 地址;
2、概述
react-navigation是react和react native專案開發的路由控制元件,用於專案的導航設定,功能強大不可估量;
三大部分:StactNavigator棧導航、TabNavigator標籤導航、DrawerNavigator抽屜導航-從左側滑出的介面;
3、棧導航常用的方法和方案
A頁面跳轉到B頁面,入棧操作,不傳遞引數:
this.props.navigation.navigate('B');//A頁面沒有銷燬,componentWillUnMount方法沒有執行,如果從B頁面goBack回A頁面,A頁面的所有狀態都保持原樣;B頁面首次執行,依次執行constructor-componentWillMount-render-componentDidMount
A頁面跳轉到B頁面,入棧操作,傳遞引數:
this.props.navigation.navigate('B',params);//params:{key01:value01,key02:value02...},按照ES6的語法,傳遞的鍵值對物件可以寫成{key01,key01};在B頁面拿到引數的方法,this.props.navigation.state.params.key01
B頁面返回A頁面,出棧操作:
this.props.navigation.goBack();//B頁面銷燬,componentWillUnMount方法執行;A頁面還是上一次離開時的狀態,並且沒有執行任何生命週期的方法;
A頁面跳轉到B頁面,B頁面返回A頁面時,回撥傳參:
this.props.navigation.navigate('B',{callback:(data)=>{this.someFunc(data)}});
const {navigate,goBack,state} = this.props.navigation;
state.params.callback(data);
goBack();
4、高階技巧
A>B>C>D>E>F,A到F都是入棧操作,從F回到B的方案:
方案一:this.props.navigation.navigate
方案二:goBack
預設,goBack()不傳遞任何引數,返回到上一個頁面,如果傳遞引數,需要傳遞頁面的key值;react-navigation對頁面棧的唯一標記資訊是this.props.navigation.state.key,因此可以採用這個屬性記錄頁面棧。如果有跨路由返回的需要,那麼在路由棧入棧時需要在this.props.navigation.state.params屬性中向下一層路由傳遞上層所有路由的路由標識key。(如果專案中有redux,則用redux儲存每個頁面的key值,更為方便)
第一步,this.props.navigation.navigate('B',{keys:{A_previout:this.props.navigation.state.key}});
//A>B,首屏A進入B頁面,儲存首屏的this.props.navigation.state.key(Init-id-APP打包的時間戳-本次應用進入的累計次數(如果殺掉程序,首次進入是0,退出應用二次進入是1))資訊在路由資訊this.props.navigation.state.params.keys中,並命名為A_previout,由於this.props.navigation.goBack(key)中需要的的key是返回到指定頁面中下一個頁面的key
第二步,this.props.navigation.navigate('C',{keys:{...this.props.navigation.state.params.keys,A:this.props.navigation.state.key}});//B>C
第三步,this.props.navigation.navigate('D',{keys:{...this.props.navigation.state.params.keys,B:this.props.navigation.state.key}});//C>D
第四步,this.props.navigation.goBack(this.props.navigation.state.params.A);//D>A
可以看出來,上面的操作方案非常繁瑣,因此需要進行優化;goBack返回到指定頁面最簡單的操作是傳遞routeName,但需要改寫原始碼,將需要的key換成routeName;
把專案/node_modules/react-navigation/src/routers/StackRouter.js檔案裡的
const backRoute = state.routes.find((route: *) => route.key === action.key);
改成
const backRoute = state.routes.find(route => route.routeName === action.key);
如果返回一級使用goBack(null),如果返回多級,使用goBack(routeName);
方案三:重置路由棧
import {NavigationActions} from 'react-navigation'
consot resetNavigationToRouteName = NavigationActions.reset({
index:1,
actions:[NavigationActions.navigate({routeName:'hp'}),NavigationActions.navigate({routeName:'secondPage'})]
})
this.props.navigation.dispatch(resetNavigationToRouteName);
缺點,會一次性重新整理actions陣列中的所有頁面,即使頁面不是當前顯示的頁面;
5、入棧需求是A>B>C>D>E,出棧需求是E>B,並且重新整理B
6、入棧需求是A>B>E,出棧需求是E>C,並且銷燬E
7、自定義頁面跳轉動畫
使用StackNavigator作為路由跳轉時,react-navigation元件提供了三種內建動畫:forHorizontal-入棧操作從右向左,出棧操作從左向右;forVerbical-入棧操作從下向上,出棧操作從上到下;forFadeFromBottomAndroid-入棧操作從底部淡出;
通過傳遞引數讓頁面使用不同的跳轉動畫:
this.props.navigation.navigate('A',{transition:'forHorizontal'});
StackNavigator({
transitionConfig:()=>{
screenInterpolator: (scenProps)=>{
const { scene } = sceneProps;
const { route } = scene;
const params = route.params || {};
const transition = params.transition || 'forHorizontal';
return CardStackStyleInterpolator[transition](sceneProps);
}
}
})
8、回退重新整理
在採用react-native-navigation路由元件時,路由棧資訊是A>B>C>D,使用goBack()方法從D頁面到C頁面,D頁面執行了ComponentWillUnMount()方法,並且D頁面銷燬了,進入了C頁面時,C頁面的所有狀態都處於保持狀態,同時頁面並沒有重新整理,此時的需求,需要重新整理C頁面。
實現方案一:使用DeviceEventEmitter
實現方案二:使用this.props.navigation.state.params.callback(); goBack();
9、標籤導航
標籤導航icon不顯示的問題,由於語法錯誤,es6的箭頭函式語法,函式體有兩種寫法,()和{},前者不用寫return直接返回內容,後者需要寫return才能返回內容;
tabBarIcon: () => (<Image resizeMode='contain' source={require('./src/img/cart-unselected.png')}/>)
tabBarIcon: () => {
return(
<Image resizeMode='contain' source={require('./src/img/cart-unselected.png')}/>
)
}
標籤導航設定預設頁面後,進入到其他標籤頁,點選物理返回按鈕,應該退出應用,但返回到首頁的問題:
10、路由高階配置之:一級登陸頁面採用棧導航,二級頁面進入主程式採用標籤導航,三級頁面採用棧導航
頁面頂層註冊棧導航:
import {StackNavigator} from 'react-navigation';
const RootStact = StackNavigator(RouteConfigs,StactNavigatorConfig);
登入頁面跳轉採用NavigationActions,登陸成功的操作:
const resetAction = NavigationActions.reset({
index: 0,
actions: [NavigationActions.navigate({routeName:'Home'})]
});
this.props.navigation.dispatch(resetAction);
進入程式主介面,是底部標籤導航的佈局:
import {TabNavigator} from 'react-navigation';
export default Home =TabNavigator(RouteConfigs,TabNavigatorConfig);
主介面下面的二級頁面,退出程式主介面重新登陸的操作:
const resetAction =NavigationActions.reset({
index: 0,
actions: [NavigationActions.navigate({routeName:'Login'})]
});
this.props.navigation.dispatch(resetAction);
這樣就完成了一個大型應用的基礎路由配置,在正式開發之前做好路由設計至關重要;
備註:關於StackNavigator-RouteConfigs、StactNavigatorConfig、TabNavigator-RouteConfigs、TabNavigatorConfig這四項的配置方法和屬性設定,如果出現技術上的問題,可以隨時聯絡和諮詢我13161854871
11、路由高階配置之:一級頁面進入主程式採用標籤導航,二級頁面採用棧導航
頁面頂層註冊標籤導航,二級頁面分別註冊棧導航,具體方案和步驟可參考上面
12、設定Title
方案一:static navigationOptions = {header: null};
方案二:
this.props.navigation.setOptions({
headerTitle: '',
headerTitleStyle:{},
headerLeft: (<View></View>),
headerRight: (<View></View>),
});
13、NavigationActions的弊端
案例:頁面棧資訊A-B-C-D,在D頁面採用this.props.navigation.dispatch(resetAction);
const resetAction = NavigationActions.reset({
index: 0,
actions: [
NavigationActions.navigate({ routeName: 'A'})
]
});
這個方案很影響效能,因為dispatch之後ABC頁面都重新渲染後(觸發componentWillMount,render,componentDidMount),才返回到A頁面;
14、goBack()到指定頁面的快捷方案
案例:頁面棧資訊A-B-C-D,在D頁面返回A頁面,執行下面方法3次即可,傳遞null是關鍵:
this.props.navigation.goBack(null);
但還有個弊端,如果不清楚頁面棧內容到底有幾層,還是無法精確返回到指定頁面;
15、修改react-navigation元件原始碼,返回到指定頁面
修改/node_modules/react-navigation/src/routers/StackRouter.js檔案286行程式碼為下面的內容:
if (action.type === NavigationActions.BACK) {
let backRouteIndex = null;
if (action.key) {
const backRoute = state.routes.find(
/* $FlowFixMe */
/* 修改原始碼 */
route => route.routeName === action.key
/* (route: *) => route.key === action.key */
);
/* $FlowFixMe */
console.log('backRoute =====',backRoute);
backRouteIndex = state.routes.indexOf(backRoute);
console.log('backRoute =====',backRouteIndex);
}
if (backRouteIndex == null) {
return StateUtils.pop(state);
}
if (backRouteIndex >= 0) {
return {
...state,
routes: state.routes.slice(0, backRouteIndex+1),
index: backRouteIndex - 1 + 1,
};
}
}
案例:頁面棧資訊A-B-C-D,在D頁面返回A頁面:
this.props.navigation.navigate('A');
但如果返回到上層頁面必須使用this.props.navigation.goBack(null);
同時,如果在滑動返回的時候,專案有可能會卡死;請注意使用該方法整合redux。
參考https://www.jianshu.com/p/2f575cc35780
未完,待續...