React-Native开发总结-react-navigation应用与实践
2017-10-29 14:03
696 查看
最近更新时间: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
未完,待续...
做开发的同学都知道,难的不是技术本身,而是产品需求的频繁变更和逻辑复杂度,这让研发工程师最为苦恼。但总体上来说,积累技术经验,善于总结和记录技术的实践心得,也是一件优雅和愉悦的事情。
对于开发的同学来说,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
未完,待续...
相关文章推荐
- Windows下使用AndroidStudio+ReactNative开发Android应用
- React-Native开发总结-组件高级使用方案汇总
- Windows下使用AndroidStudio+ReactNative开发Android应用
- React Native布局实践:开发京东客户端首页遇到的坑
- React Native入门——布局实践:开发京东客户端首页(二)TabBar的构建
- Redux中间件在React和React-Native项目架构中的应用实践
- React-Native学习笔记——react-redux最佳实践应用篇
- 基于Redux的ReactNative项目开发总结(一)
- react-native总结之项目开发环境创建
- windows部署React-Native的开发环境实践(技术细节)
- Android 原生应用嵌入React-Native模块开发-环境配置及填坑记
- React Native跨平台移动应用学习与开发(一) 环境的搭建
- 20169205 2016-2017-2 《移动平台应用开发实践》第8周学习总结
- 【React Native开发】React Native应用设备执行(Running)以及调试(Debugging)(3)
- 基于Redux的ReactNative项目开发总结(二)
- Android原生应用集成ReactNative坑总结
- React Native for Android on Windows 配置开发安装总结
- 第一次react-native项目实践要点总结
- React-Native实际项目开发中的高效率调试实践方案
- Qt Quick应用开发介绍 10-12(动态界面, 实践学习, 总结和扩展)