react-navigation

16 12月

转自这里–React Navigation 学习

基于React Navigation 4.x版本

RN中原本内置的路由组件已经独立成了react-navigation,提供了很多路由的方式:

  • 基础API
  • Stack
  • Tab
  • Drawer
  • Switch
  • 路由传递
  • 滴滴路由设计

基础API

当路由器里的页面被打开后,this.props.navigation里会包含以下功能:

  • navigate:路由跳转
  • goBack:关闭当前页,返回前一页
  • addListener:订阅路由的生命周期(willFocus,didFocus,willBlur,didBlur)的更新
  • isFocused:返回true/false表示页面获取了焦点
  • state:返回当前页面通过navigate方法传来的{routeName, params, key}
  • getParam / setParams:可以增加或改变navigate方法传来的params
  • dispatch:发送一个action来跳转路由
  • dangerouslyGetParent:返回父路由器

navigate({routeName,params,action,key}) / navigate(routeName, params, action):页面间跳转。参数routeName:页面路由名。params:要传给下一个页面的参数。key:配合goBack使用,goBack可以从指定key的页面返回上一页。

下图说明Stack中的navigate的行为。当栈内没有找到该路由对应的页面时,就推入一个新的页面,否则只是弹出到已有页面。(Tab,Drawer底层也是Stack实现,一个路由只能有一个组件存在,但this.props.navigation.state永远都是所有路由的集合)

goback(key):关闭当前页,返回前一页。通常是不传参的,或传null。也可以传参,参数是上述navigate跳转时设置的key,这样就能返回到指定key的路由。例如:

getParam / setParams:可以增加或改变navigate方法传来的params

class ProfileScreen extends React.Component {
  render() {
    return (
      <View>
        <Text>{this.props.navigation.getParam('title')}</Text>
        <Button
          onPress={() => this.props.navigation.setParams({ name: 'Zhang' })}
          title="改变参数"
        />
      </View>
    );
  }
}

dispatch:通常前进返回用navigate或goBack就可以了,但当你无法用这两个方法时,可以用dispatch个NavigationActions来打个补丁。NavigationActions提供了navigate,back,setParams三个方法,用法和this.props.navigation里方法用法相同,不赘述。区别是:navigation.setParams只能修改当前页面的params,但NavigationActions.setParams可以修改所有页面的Params。

import { NavigationActions } from 'react-navigation';

const navigateAction = NavigationActions.navigate({
  routeName: 'TestPage',
  params: {},
  key: "key-test",
});
const backAction = NavigationActions.back();

this.props.navigation.dispatch(navigateAction);
...
this.props.navigation.dispatch(backAction);

dangerouslyGetParent:返回父路由,为防止滥用方法名被定义为dangerously。一个应用场景是:当已经到了栈顶页面,没有父路由时,不显示回退按钮,禁止手势回退功能。

class UserCreateScreen extends Component {
  static navigationOptions = ({ navigation }) => {
    const parent = navigation.dangerouslyGetParent();
    const gesturesEnabled =
      parent &&
      parent.state &&
      parent.state.routeName === 'StackWithEnabledGestures';

    return {
      title: 'New User',
      gesturesEnabled,
    };
  };
}

Stack

路由器有很多种:Stack,Tab,Drawer,Switch,你可以选择一个或组合多个来使用。

最常用的是页面顶部的Stack路由器,每一个页面有一个标题栏,有和Web路由一致的路由历史管理方式,还支持响应用户手势的操作。this.props.navigation里有一些额外的功能:

  • push(routeName, params, action):也是页面跳转,和navigate区别是:navigate如果去往的路由在栈中存在,会直接返回到这条路由。push永远在栈顶增加新路由,所以相同路由可以在栈中出现多次
  • pop(n):返回路由堆栈中的上一个页面
  • popToTop():直接跳到栈底
  • replacereplace(routeName, params, action):用新路由替换当前路由
  • reset:重置当前navigator
  • dismiss:退出当前navigator,返回上层navigator。和reset的区别一图胜千言:

需要单独安装react-navigation-stack,通过createStackNavigator方法来创建Stack路由器。

createStackNavigator(RouteConfigs, StackNavigatorConfig)有两个参数:RouteConfigs配置路由对象(如路由名,路由path规则等),StackNavigatorConfig配置路由转场动画,页头导航条的样式等。都比较简单直接看官网文档照着配就行。

Tab

Tab(BottomTab / MaterialBottomTab / MaterialTopTab)导航栏。

BottomTab底部tab,相当于IOS里面的UITabBarController,需要单独安装react-navigation-tabs,通过createBottomTabNavigator方法来创建BottomTab路由器。

createBottomTabNavigator(RouteConfigs, BottomTabNavigatorConfig):

RouteConfigs(必须)参数:可以设置screen,path,navigationOptions(配置全局的屏幕导航选项如:title,headerRight,headerLeft,tabBarLabel,tabBarIcon等)

BottomTabNavigatorConfig(可选)参数:可以设置tabBar的样式,排列顺序,点后退按钮是否切换到之前的tab等。

MaterialTopTab顶部tab,主要用于顶部主题菜单,需要单独安装react-navigation-tabs,通过createMaterialTopTabNavigator方法来创建MaterialTopTab路由器。

createMaterialTopTabNavigator(RouteConfigs, TabNavigatorConfig):

RouteConfigs(必须)参数:可以设置screen,path,navigationOptions(配置全局的屏幕导航选项如:title,headerRight,headerLeft,tabBarLabel,tabBarIcon等)

TabNavigatorConfig(可选)参数:可以设置tabBar的样式,是否支持左右滑动,是否支持懒加载,点后退按钮是否切换到之前的tab等。

Drawer

侧边抽屉导航栏,主要用于顶部主题菜单。this.props.navigation里有一些额外的功能:

  • openDrawer:打开抽屉
  • closeDrawer:关闭抽屉
  • toggleDrawer:打开/关闭抽屉

需要单独安装react-navigation-drawer,通过createDrawerNavigator方法来创建Drawer路由器。

createDrawerNavigator(RouteConfigs, DrawerNavigatorConfig):

RouteConfigs(必须)参数:可以设置screen,path,navigationOptions(配置全局的屏幕导航选项如:title,headerRight,headerLeft,tabBarLabel,tabBarIcon等)

DrawerNavigatorConfig(可选)参数:可以设置侧边栏样式,过场动画,点后退按钮是否切换到之前的tab等。

Switch

Switch的用途是一次只显示一个页面,常见启动屏,或登录屏,react-navigation自带,不需要额外安装。通过createSwitchNavigator方法创建Switch路由器。

路由传递

上面的这些属性都是在screen组件中,通过this.props.navigation调用的。如果有深层次的子组件想操作路由,screen就需要将navigation作为子组件的属性传递下去。以下提供了一些不传属性也能操作路由的方法:

withNavigation:高阶组件,对内传递给子组件navigation属性,对外暴露onRef属性传递出子组件的引用

import React from 'react';
import { Button } from 'react-native';
import { withNavigation } from 'react-navigation';

class MyBackButton extends React.Component {
  render() {
    return (
      <Button
        title="Back"
        onPress={() => {
          this.props.navigation.goBack();
        }}
      />
    );
  }
}
export default withNavigation(MyBackButton);

// 使用
<MyBackButton onRef={elem => (this.backButton = elem)} />;

withNavigationFocus:高阶组件,对内传递给子组件isFocused属性。注意,由于是属性传递,会导致组件重新渲染,需要 shouldComponentUpdate来控制组件渲染次数。

import React from 'react';
import { Text } from 'react-native';
import { withNavigationFocus } from 'react-navigation';

class FocusStateLabel extends React.Component {
  render() {
    return <Text>{this.props.isFocused ? 'Focused' : 'Not focused'}</Text>;
  }
}

export default withNavigationFocus(FocusStateLabel);

全局变量:将某个navigator保存为全局变量,这样不同层级的页面也可以方便地互相导航。

// NavigationService.js
import { NavigationActions } from 'react-navigation';

let _root;
const setTopLevelNavigator = (navigatorRef) => {
    _root = navigatorRef;
}
const getTopLevelNavigator = () => {
    return _root
}
export default {
  setTopLevelNavigator,
  getTopLevelNavigator
};

// App.js
const App = () => {
  return (
    <RootNavigator ref={navigation.setTopLevelNavigator} />
  );
};

// Page.js
const navigator = navigation.getTopLevelNavigator();
navigator.dispatch(NavigationActions.navigate({
    routeName: 'Drawer',
    action: DrawerActions.openDrawer()
}))

滴滴路由设计

设计个滴滴的路由框架:

首先,我们有一个广告页、登录页、主页的选择的场景,这三个页面是互斥的,只会存在一个,这种场景就适合用SwitchNavigator。

顺风车、出租车明显是TopTabNavigator的交互——注意它们的上方还有一个类似标题栏的东西,这意味着可以在外面再套一层StackNavigator(订单页也是如此)。

而这一层StackNavigator和订单页的StackNavigator都是属于DrawerNavigator的内容,于是我们就有了下图这样一个路由的结构。

发表评论

电子邮件地址不会被公开。 必填项已用*标注