tabs

React Navigation V5 详解

2020-04-28  本文已影响0人  马六甲的笔记

安装


yarn add react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view @react-navigation/native

官方文档中提到的 yarn add react-native-reanimated,该依赖在使用 DrawerNavigator 才用的到,不晓得为啥放到了总的安装文档中,或许后期升级可能会用到,尽量也装上。

安装完之后,在 js 入口文件,如 index.js 顶部添加 import 'react-native-gesture-handler';,少了这一句,可能会导致生产环境 app 出现闪退现象。

在 App.js 中添加以下代码,激活 react-native-screens 的原生端

import { enableScreens } from 'react-native-screens';
enableScreens();

完事儿,与 V3 / V4 不同,不需要修改其他文件了。

使用


先看以下一段伪代码:NavigationContainer 下的根导航为 Stack.Navigator,该导航包裹了两个屏幕;第一个屏幕为 bottomTab 的页面,该屏幕本身包含两个页面; 第二屏幕为一个普通 Page

import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';

const Tab = createBottomTabNavigator();
const Stack = createStackNavigator()

const Main = () => (<Tab.Navigator
    initialRouteName={String}
    [children={Component}]
    screenOptions={}

    backBehavior={true}
    lazy={}
    tabBar={}
    tabBarOptions={}
>
  <Tab.Screen
      name={String}
      options={}
      listeners={}
      component={} || children={}
      initialParams={}
  />
</Tab.Navigator>);


const App = () => (<NavigationContainer
  theme={theme}
  initialState={initialState}
  onStateChange={newState => {} }
  [children={Component}]
  ref={}
  independent={boolean}
>
  <Stack.Navigator
    initialRouteName={String}
    [children={Component}]
    screenOptions={}

    mode={}
    headerMode={}
    keyboardHandlingEnabled={}
  >
    <Stack.Screen 
      name="Main"
      component={Main}
    />
    <Stack.Screen 
      name={String}
      options={}
      listeners={}
      component={} || children={}
      initialParams={}
    />
  </Stack.Navigator>
</NavigationContainer>)

V5 版本相比之前,结构更加清晰,彻底将底层逻辑与导航页面分离,底层逻辑由 @react-navigation/native (该模块依赖了 @react-navigation/core@react-navigation/routers)提供;

页面由导航器提供,方便自行定义,官方提供了 stackdrawerbottom-tabsmaterial-bottom-tabsmaterial-top-tabs 五个导航器,可满足大部分需求了,根据需要自行安装即可。

根据以上代码,结合源码阅读,说明一下具体含义

一、 NavigationContainer


该组件在 @react-navigation/native 中定义,为顶层容器,一般情况,一个 APP 只有一个,参考 官方文档,该组件支持以下属性

1. theme

主题,该属性由 @react-navigation/native 缓存,但并未有任何作用,会下发到导航器,由导航器获取并加以利用,默认为以下属性:

theme={
    dark: boolean;
    colors: {
        primary: string;
        background: string;
        card: string;
        text: string;
        border: string;
    };
}

2. initialState

自定义传入变量,多用于 deepLink,该项暂未验证

3. onStateChange

导航状态变化的监听函数,可用于页面统计或其他操作

3. children

子组件(导航器 Navigator 组件),该项一般使用 jsx 直接插入,而不是通过 props 传递,比如上面的示例,childrenStack.Navigator

4. ref

获取 NavigationContainer 实例,用于调用实例 api,可通过 console.log 打印可用 api

5. independent

此导航容器是否应独立于父容器,如果未将其设置为“ true”,则此容器不能嵌套在另一个容器中,并且会断开所有子级导航器与父容器的连接。
该项并未在官方文档中发现,但源码中可以看到,不建议使用,一般情况也很少有多个顶级容器。

二、Navigator


导航器的根组件,用于包裹导航器下的页面;通过阅读源码可以知道所有导航器都支持:

1. initialRouteName

导航器默认要显示的 screen

2. children

导航器包裹的 screens,通常不会使用 Props 传递,而是在 jsx 中实现。

3. screenOptions

参看以上伪代码,导航器包裹的 screen 属性是一样的,都有一个 options 属性,而 screenOptionsoptions 的值是完全相同的,作为 options 的默认值。

阅读 @react-navigation/core 源码,这个参数的值可以是 ObjectFunction({route, navigation}) 返回 Object,且未对 Object 字段做任何限制,而只是为导航器的实现提供了一个顶层 API,比如官方的两个实现:

screenOptions = {
    title, header, headerShown, ........
}

// 或通过函数返回
screenOptions = { ({route, navigation}) => {
    return {
      title, header, headerShown, ........
    }
}}

以上为基础属性,适用于所有导航器,不同的导航器会在此基础中拓展额外的属性:

4. @react-navigation/stack

拓展了 mode / headerMode / keyboardHandlingEnabled 属性(文档

5. @react-navigation/bottom-tabs

继承 @react-navigation/routers 的拓展属性 backBehavior

拓展了 lazy / tabBar / tabBarOptions 属性(文档

三、Screen


导航器内的具体页面,该组件是在 @react-navigation/core 中实现的,与导航器无关,所有导航器的 screen 都支持且仅支持以下属性

1. name

页面名称,可用于导航跳转

2. options

Stack.Navigator 中的 screenOptions 相同,单独设置来覆盖 screenOptions 的配置;同样的,可以设置为 ObjectFunction;具体结构由 Screen 所属的 Navigator 类型决定。

3. listeners

监听 screen 事件,具体会收到什么回调由所属的 Navigator 类型决定,如 @react-navigation/stackevents@react-navigation/bottom-tabsevents;该属性的值与 options 有点类似,可以指定为 ObjectFunction,如

listeners={{
    tabPress: e => {
      // Prevent default action
      e.preventDefault();
    },
}}

// 或通过函数返回
listeners={({ navigation, route }) => {
    return {
        tabPress: e => {
          // Prevent default action
          e.preventDefault();
        },
    }
}}

3. component / children

Screen 绑定的组件, 可通过 component 指定组件对象,或直接使用 children 定义组件;二者互斥,一般使用 component 属性来定义

4. initialParams

传递给 Screen 组件的初始化 params

四、接口


d

五、StackNavigator options


与 Header 组件相关的属性

标题

左侧返回组件

右侧自定义组件

标题栏整体属性

与页面组件相关的属性

与页面切换效果相关的属性

与页面切换手势相关的属性

切换效果

由以上属性可以看出,页面过渡效果由以下属性共同构成:

可通过这三个属性实现不同的过渡效果;另外若 gestureEnabled=true,意味着不同过渡效果也要设置相匹配的 gestureDirection; 所以可以将这四个属性配置为一组,方便直接调用,如:

const transition = {
     gestureDirection:"horizontal",
     transitionSpec: {},
     cardStyleInterpolator:() => {},
     headerStyleInterpolator:() => {},
}
<Stack.Navigator
    screenOptions={
       cardStyle:{},
       gestureEnabled:true,
       ...transition
    }
>
    <Stack.Screen />
</Stack.Navigator>

React Navigation 的设计初衷应该也在于此,所以其默认提供了几组属性,可以直接使用。

使用方法:

import { TransitionPresets  } from '@react-navigation/stack';

<Stack.Navigator
    screenOptions={
       cardStyle:{},
       gestureEnabled:true,
       ...TransitionPresets.SlideFromRightIOS,
    }
>
    <Stack.Screen />
</Stack.Navigator>

若对这些默认提供的效果都不满意,那只能自定义了。

1、transitionSpec

动效 config 配置可参考 timingspring

const config = {
  animation: 'timing || spring',
  config: {

    // animation="timing" 支持:
    duration:1000,
    easing: Easing.ease,
   
    // animation="spring" 支持:
    stiffness: 1000,
    damping: 500,
    mass: 3,
    overshootClamping: true,
    restDisplacementThreshold: 0.01,
    restSpeedThreshold: 0.01,

  },
};

transitionSpec = {
    open: config,   // 新页面弹出时动效
    close: config,   // 新页面收回时动效,一般二者为同一个
};


// React Navigation 提供了几个默认的,可直接使用或作为参考
import { TransitionSpecs } from '@react-navigation/stack';
transitionSpec = TransitionSpecs.TransitionIOSSpec 
transitionSpec = TransitionSpecs.FadeInFromBottomAndroidSpec 
transitionSpec = TransitionSpecs.FadeOutToBottomAndroidSpec 
transitionSpec = TransitionSpecs.RevealFromBottomAndroidSpec 

2、cardStyleInterpolator

通过函数返回以下样式

cardStyleInterpolator = () => {
    return  {
          containerStyle:{},
          cardStyle:{},
          overlayStyle:{},
    }
}

// React Navigation 提供了几个默认的,可直接使用或作为参考
import { CardStyleInterpolators } from '@react-navigation/stack';
cardStyleInterpolator = CardStyleInterpolators.forHorizontalIOS
cardStyleInterpolator = CardStyleInterpolators.forVerticalIOS 
cardStyleInterpolator = CardStyleInterpolators.forModalPresentationIOS 
cardStyleInterpolator = CardStyleInterpolators.forFadeFromBottomAndroid 
cardStyleInterpolator = CardStyleInterpolators.forRevealFromBottomAndroid 

3、HeaderStyleInterpolators

通过函数返回以下样式

HeaderStyleInterpolators = () => {
    return  {
          leftButtonStyle:{},
          leftLabelStyle:{},
          titleStyle:{},
    }
}

// React Navigation 提供了几个默认的,可直接使用或作为参考
import { HeaderStyleInterpolators } from '@react-navigation/stack';
HeaderStyleInterpolators = HeaderStyleInterpolators.forUIKit 
HeaderStyleInterpolators = HeaderStyleInterpolators.forFade 
HeaderStyleInterpolators = HeaderStyleInterpolators.forStatic 

以上三个属性可全部自定义,也可以部分自定义,部分使用 React Navigation 提供的预置,最后在添加一个 gestureDirection 属性就可构成一组自定义页面切换效果,非常的方便。

上一篇下一篇

猜你喜欢

热点阅读