您的位置:首页 > Web前端 > React

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

未完,待续...
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: