美文网首页tabs
React Navigation V5 详解

React Navigation V5 详解

作者: 马六甲的笔记 | 来源:发表于2020-04-28 21:03 被阅读0次

    安装


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

    • react-native-gesture-handler 用于手势切换页面
    • react-native-screens 用于原生层释放未展示的页面,改善 app 内存使用
    • react-native-safe-area-context 用于保证页面显示在安全区域(主要针对刘海屏)
    • @react-native-community/masked-view 用在头部导航栏中返回按钮的颜色设置
    • @react-navigation/native 为 React Navigation 的核心

    官方文档中提到的 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


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

    • @react-navigation/routers 定义的 initialRouteName
    • @react-navigation/core 定义的 children / screenOptions

    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 组件相关的属性

    标题

    • title: string, 标题文字
    • headerTitleAlign: 标题对齐方式,支持 left (Android 默认) / center (iOS 默认)
    • headerTitleAllowFontScaling: 标题文字是否随系统文字大小缩放
    • headerTintColor: 标题颜色
    • headerTitleStyle: 自定义标题文字样式
    • headerTitleContainerStyle: 自定义标题文字所在 View 容器的样式
    • headerTitle: 标题,可直接设置文字,优先级高于 title;也可以设置为函数,返回一个组件,函数参数为 {allowFontScaling, style, children},这三个参数是由上面属性结合而来。

    左侧返回组件

    • headerBackImage: 返回键,设置为一个函数,返回“返回键”组件,函数参数为 {tintColor:"标题颜色"}
    • headerBackTitle: string 返回键右侧的返回文字
    • headerTruncatedBackTitle: 返回文字过长,标题栏无法显示时的替代返回文字,默认: "Back"
    • headerBackAllowFontScaling: 返回文字是否随系统文字大小缩放
    • headerBackTitleStyle: 自定义返回文字样式
    • headerBackTitleVisible: 是否显示返回文字,Android 默认 false,iOS 默认 true
    • headerPressColorAndroid: Android 5 以上,点击返回按钮的水波纹颜色
    • headerLeftContainerStyle: 自定义返回键和返回文字所在容器的样式
    • headerLeft: 自定义 HeaderBackButton 左侧组件,指定为函数 或 RN组件,props 会传递上面的返回键和返回文字相关的设置

    右侧自定义组件

    • headerRight: 自定义标题栏右侧组件
    • headerRightContainerStyle: 自定义右侧组件所在容器的样式

    标题栏整体属性

    • headerStatusBarHeight: 设置 statusBar 高度,Header 组件会 paddingTop 这个值以保证在刘海屏机型也可以正常使用,默认会由系统自动获取。
    • headerStyle: 自定义标题栏样式
    • headerTransparent: 标题栏是否透明,与在 headerStyle 直接设置 backgroundColor 的不同在于:这里设置透明,会使页面的 marginTop 为 0,此时需要定义 headerBackground 组件来遮挡。
    • headerBackground: 标题栏背景组件,配合 headerTransparent 使用的,可以用来实现毛玻璃 Header 效果。
    • safeAreaInsets: Header安全区域设置(针对刘海屏机型),默认情况下会自动设置,但也可以使用 {left, right, top, bottom} 手动设置,自定义设置注意考虑横竖屏的情况。
    • headerShown: 是否显示标题栏
    • header: 自定义标题栏组件,定义为函数,返回一个 RN 组件;设置该属性,即不使用默认 Header 了,以上属性失效。

    与页面组件相关的属性

    • cardStyle: 页面 Card 的样式
    • cardShadowEnabled: 是否显示 Card 边缘的阴影组件;该组件是一个宽度为 3、紧贴边缘、使用 style.shadowOffset 定义阴影的 view 组件,所以该组件目前不支持 android,默认也仅在 iOS 上开启。
    • cardOverlayEnabled: 是否在 Card 下方添加一个组件(也就是在前一个 Card 的上方添加一个组件)
    • cardOverlay: 函数,返回 cardOverlayEnabled=true 要覆盖的组件,该组件可用于页面切换时的效果设定,比如一个黑色的 view,切换过程中逐渐透明,甚至是毛玻璃组件,下方页面就呈现出一种逐渐显示的感官。

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

    • animationEnabled: 是否使用页面切换动效,该属性在 web 端为关闭状态
    • animationTypeForReplace: 动画切换过程使用的类型: "push" 或 "pop"
    • cardStyleInterpolator: 页面过渡的动画效果配置;配置动画函数,时长等。
    • cardStyleInterpolator: 函数,返回页面过渡过程中 Card 内相关组件的插值样式。
    • headerStyleInterpolator: 函数,返回页面过渡过程中 Header 内相关组件的插值样式。

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

    • gestureEnabled: 是否支持手势返回,iOS默认开启(不开启的话只能在页面上自定义返回按钮了),Android 默认是关闭的(Android 除了返回按钮,还有物理/虚拟返回键)
    • gestureDirection: 返回的手势滑动方向,支持以下值
      • horizontal: 从左到右
      • horizontal-inverted: 从右到左
      • vertical: 从上到下
      • vertical-inverted: 从下到上
    • gestureResponseDistance: 从边缘为起点,支持手势返回的距离,格式为 {horizontal:25, vertical:135};比如手势方向 gestureDirectionhorizontal,那么只有在左边缘 25 以内的区域向右滑动才会响应。
    • gestureVelocityImpact: 触摸返回的手速设置,在手速低于该值时,滑动距离需大于滑动方向上尺寸的 50% 才会返回到上一页,否则弹回;高于所设置手速,即使滑动距离未达到50%,也会返回到上一页面;默认值为 0.3

    切换效果

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

    • transitionSpec
    • cardStyleInterpolator
    • headerStyleInterpolator

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

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

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

    • SlideFromRightIOS: 仿原生 iOS 左右滑动的过渡效果
    • ModalSlideFromBottomIOS: 仿原生 iOS Modal 上下滑动的过渡效果
    • ModalPresentationIOS: 仿原生 iOS 13 Modal 卡片的上下滑动过渡效果,ModalSlideFromBottomIOS 为下方页面不动,新页面从底部滑出至顶部;而 ModalPresentationIOS,下方页面会有小幅缩小,新页面弹出后,最顶端会保留一段距离,感官上如同弹出一个卡片,知乎的查看评论就是这个效果;该效果在 android 上目前无法完美使用,因为 react-native-screens 会回收旧页面,所以下方页面被回收,导致弹出卡片底部为空白,除非不通过 enableScreens() 启用回收特性,但这又影响了性能。
    • FadeFromBottomAndroid: 仿原生 Android Oreo 上下滑动的过渡效果,新页面从底部滑出,由半透明逐渐不透明。
    • RevealFromBottomAndroid: 仿原生 Android Pie 上下滑动的过渡效果,新页面从底部逐渐展开。
    • DefaultTransition: 当前的默认过渡效果,android 根据系统版本为 FadeFromBottomAndroidRevealFromBottomAndroid,iOS 为 SlideFromRightIOS
    • ModalTransition: 在 Stack.Navigator.mode="modal" 时的默认过渡效果,android 同上, iOS 为 ModalSlideFromBottomIOS

    使用方法:

    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

    通过函数返回以下样式

    • containerStyle: Card 所在 Animated.View 容器的样式
    • cardStyle: Card 样式
    • overlayStyle: 在 cardOverlayEnabled=true 时,自定义 cardOverlay 组件的样式
    • shadowStyle: 在 cardShadowEnabled=true 时,Card 边缘的阴影组件样式
    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

    通过函数返回以下样式

    • backgroundStyle: Header 背景组件的样式
    • leftButtonStyle: Header 左侧返回键所在 Animated.View 容器的样式
    • leftLabelStyle: Header 返回键旁边的返回文字所在 Animated.Text 的样式
    • titleStyle: Header 标题所在 Animated.View 容器的样式
    • rightButtonStyle: Header 右侧 Animated.View 容器的样式
    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 属性就可构成一组自定义页面切换效果,非常的方便。

    相关文章

      网友评论

        本文标题:React Navigation V5 详解

        本文链接:https://www.haomeiwen.com/subject/zaubwhtx.html