react-navigation V5 总结
注: 5x版本不同于4x版本,包括包,以及使用方法都有较大的改变。
安装
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-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
的原生端,与4x版本不同,不需要修改其他文件了
import { enableScreens } from 'react-native-screens';
enableScreens();
简单使用
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>)
详解
一.NavigationContainer
该组件在 @react-navigation/native
中定义,为顶层容器,一般情况,一个 APP 只有一个(以下是该标签的属性)
0.them
主题,该属性由 @react-navigation/native
缓存,但并未有任何作用,会下发到导航器,由导航器获取并加以利用,默认为以下属性:
theme={
dark: boolean;
colors: {
primary: string;
background: string;
card: string;
text: string;
border: string;
};
}
1.initialState
自定义传入变量,多用于 deepLink,该项暂未验证
2.onStateChange
导航状态变化的监听函数,可用于页面统计或其他操作
3.children
子组件(导航器 Navigator
组件),该项一般使用 jsx 直接插入,而不是通过 props 传递,比如上面的示例,children
为 Stack.Navigator
4.ref
获取 NavigationContainer
实例,用于调用实例 api,可通过 console.log 打印可用 api
5.independent
此导航容器是否应独立于父容器,如果未将其设置为“ true”,则此容器不能嵌套在另一个容器中,并且会断开所有子级导航器与父容器的连接。
该项并未在官方文档中发现,但源码中可以看到,不建议使用,一般情况也很少有多个顶级容器。
二.Navigator
导航器的根组件,用于包裹导航器下的页面;通过阅读源码可以知道所有导航器都支持:
1. initialRouteName
导航器默认要显示的 screen
2. children
导航器包裹的 screens,通常不会使用 Props 传递,而是在 jsx 中实现。
3. screenOptions
参看以上伪代码,导航器包裹的 screen 属性是一样的,都有一个 options
属性,而 screenOptions
与 options
的值是完全相同的,作为 options
的默认值。
阅读 @react-navigation/core
源码,这个参数的值可以是 Object
或 Function({route, navigation})
返回 Object
,且未对 Object 字段做任何限制,而只是为导航器的实现提供了一个顶层 API
screenOptions = {
title, header, headerShown, ........
}
// 或通过函数返回
screenOptions = { ({route, navigation}) => {
return {
title, header, headerShown, ........
}
}}
// 以上为基础属性,适用于所有导航器,不同的导航器会在此基础中拓展额外的属性
@react-navigation/stack // 拓展了 mode / headerMode / keyboardHandlingEnabled 属性
@react-navigation/bottom-tabs // 继承 @react-navigation/routers 的拓展属性 backBehavior 拓展了 lazy / tabBar / tabBarOptions 属性
三.Screen
导航器内的具体页面,该组件是在 @react-navigation/core
中实现的,与导航器无关,所有导航器的 screen 都支持且仅支持以下属性
1.name
页面名称,可用于导航跳转
2.options
与 Stack.Navigator
中的 screenOptions
相同,单独设置来覆盖 screenOptions
的配置;同样的,可以设置为 Object
或 Function
;具体结构由 Screen 所属的 Navigator 类型决定。
3.listeners
监听 screen 事件,具体会收到什么回调由所属的 Navigator 类型决定,如 @react-navigation/stack
的 events、@react-navigation/bottom-tabs
的 events;该属性的值与 options
有点类似,可以指定为 Object
或 Function
,如
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
4.StackNavigator options 对象中属性
标题
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 默认 trueheaderPressColorAndroid
: 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 了,以上属性失效。
option中与页面相关属性
-
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}
;比如手势方向gestureDirection
为horizontal
,那么只有在左边缘 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 根据系统版本为FadeFromBottomAndroid
或RevealFromBottomAndroid
,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
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
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
属性就可构成一组自定义页面切换效果