React-Native开发总结-react-navigation应用与实践

来源:互联网 发布:mysql 查询会锁表吗 编辑:程序博客网 时间:2024/05/08 10:06

最近更新时间: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({routName:'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({routName:'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>),
});


未完,待续...