【React Native】React Navigation 5.x的使用
在App中,底部 TabBar
导航和顶部的导航栏导航是最常见的页面导航方式,而 React Native
官方推荐的第三方库是 @react-navigation
,正好今年 React Navigation
发布了5.0版本,与前面的版本差别还挺大,不过使用上更方便了。本文主要讲解 React Navigation
5.0及以上版本的使用。
安装
安装核心包
# NPM npm install @react-navigation/native # Yarn yarn add @react-navigation/native
安装依赖
# NPM npm install react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view #Yarn yarn add react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view
React Native0.60及以上版本是自动链接库的,不需要手动运行 react-native link
,但如果你用 React Native
开发的是iOS,还需要手动安装 pods
来完成库的链接:
npx pod-install ios
把下面这行代码放在入口文件的顶部,比如 index.js
或者 App.js
import 'react-native-gesture-handler';
最后需要用 NavigationContainer
将整个App包裹起来,这个代码一般也是放在入口文件,类似下面这样:
import 'react-native-gesture-handler'; import * as React from 'react'; import { NavigationContainer } from '@react-navigation/native'; export default function App() { return ( <NavigationContainer>{/* Rest of your app code */}</NavigationContainer> ); }
注意:如果你同时也使用了 Redux
框架,需要把 Provider
放在最外层,将 NavigationContainer
包裹在次外层,类似下面这样:
export default class App() { return ( <Provider store={store}> <NavigationContainer> {/* Screen configuration */} </NavigationContainer> </Provider> ); }
使用
React Navigation有多种导航方式: Stack Navigation
、 Tab Navigation
和 Drawer Navigation
。这里主要讲 Stack Navigation
和 Tab Navigation
。
顶部导航栏导航
首先需要安装相应的库:
#NPM npm install @react-navigation/stack #Yarn yarn add @react-navigation/stack
Stack Navigation
使用上相对来说比较简单,只需要把需要导航的页面组件封装成 Stack.Screen
,然后包裹在 Stack.Navigator
就可以了,最上面的 Stack.Screen
就是默认页面。
import * as React from 'react'; import { NavigationContainer } from '@react-navigation/native'; import { createStackNavigator } from '@react-navigation/stack'; const Stack = createStackNavigator(); function App() { return ( <NavigationContainer> <Stack.Navigator> <Stack.Screen key="Home" name="Home" component={HomeScreen} /> </Stack.Navigator> <Stack.Screen key="detail" name="Detail" component={DetailScreen} /> </Stack.Navigator> </NavigationContainer> ); } export default App;
也可以在 Stack.Screen
中设置导航栏的属性,设置导航栏属性 options
有两种方式,一种是带 route
参数的,一种是直接设置
/* 带route参数的options */ <Stack.Screen key={name} name={name} component={component} options={({route}) => ({ headerTitle: route.name, headerStyle: { backgroundColor: '#01aaff', }, headerTintColor: '#fff', headerTitleStyle: { fontWeight: 'bold', }, headerRight: () => ( <Button onPress={() => alert('This is a button!')} title="Info" color="#fff" /> ), headerShown: true })} /> /* 直接设置options */ <Stack.Screen key={name} name={name} component={component} options={{ headerTitle: 'name', headerStyle: { backgroundColor: '#01aaff', }, headerTintColor: '#fff', headerTitleStyle: { fontWeight: 'bold', }, headerRight: () => ( <Button onPress={() => alert('This is a button!')} title="Info" color="#fff" /> ), headerShown: true }} />
注意: React
框架使用的是虚拟 DOM
,使用 diff
算法刷新页面显示,在 Stack.Screen
中,需要添加 key
属性,否则会报警告。
在需要跳转到其他页面时只需要以下代码就可以了,后面跟的是参数。
function HomeScreen({ navigation }) { return ( <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}> <Text>Home Screen</Text> <Button title="Go to Details" onPress={() => { /* 1. Navigate to the Details route with params */ navigation.navigate('Details', { itemId: 86, otherParam: 'anything you want here', }); }} /> </View> ); }
如果是用 class
方式创建的页面,代码会像下面这样:
export default class HomeScreen extends Component { render() { return ( <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}> <Text>Home Screen</Text> <Button title="Go to Details" onPress={() => { this.props.navigation.navigate('Details', { itemId: 86, otherParam: 'anything you want here', }); }} /> </View> ); } }
注意:导航跳转可以使用 navigation.navigate
或者 navigation.push
。如果使用 navigate
,会查找当前堆栈中是否有名字一样的路由,如果没有才创建新的路由并跳转,而 push
则会直接创建一个新的路由并跳转,也就是说可以重复多次跳转同一个页面。
堆栈导航的返回方式如下:
// 返回到上一页面 navigation.goBack() // 返回到堆栈里第一个页面 navigation.popToPop()
底部导航栏导航
除了顶部导航栏,最常用的就是底部导航栏了,首先也需要安装相应的库:
# NPM npm install @react-navigation/bottom-tabs # Yarn yarn add @react-navigation/bottom-tabs
底部导航栏的使用方法类似,需要将这些页面组件包装成 Tab.Screen
,导航则由框架内部完成,不需要手动控制:
import * as React from 'react'; import { NavigationContainer } from '@react-navigation/native'; import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; const Tab = createBottomTabNavigator(); export default function App() { return ( <NavigationContainer> <Tab.Navigator> <Tab.Screen name="Home" component={HomeScreen} /> <Tab.Screen name="Settings" component={SettingsScreen} /> </Tab.Navigator> </NavigationContainer> ); }
底部导航栏属性可以按如下方式设置:
<Tab.Navigator screenOptions={({ route }) => ({ tabBarIcon: ({ focused, color, size }) => { let iconName; if (route.name === 'Home') { iconName = focused ? 'ios-information-circle' : 'ios-information-circle-outline'; } else if (route.name === 'Settings') { iconName = focused ? 'ios-list-box' : 'ios-list'; } return <Ionicons name={iconName} size={size} color={color} />; }, })} tabBarOptions={{ activeTintColor: 'tomato', inactiveTintColor: 'gray', }} > <Tab.Screen name="Home" component={HomeScreen} /> <Tab.Screen name="Settings" component={SettingsScreen} /> </Tab.Navigator>
顶部堆栈导航栏与底部导航栏嵌套
Stack Navigation
和 Tab Navigation
可以相互多层嵌套,比如登录页跳转到带底部导航栏的主页面,需要把登录相关面和 Tab.navigator
一同放到同一个堆栈中,但官方不推荐这种方式,嵌套层次太多会导致维护起来特别麻烦。官方推荐使用一个变量控制当前显示的页面,把不相关的模块隔离在不同的堆栈中,类似下面这样:
const commonScreens = { Help: HelpScreen, }; const authScreens = { SignIn: SignInScreen, SignUp: SignUpScreen, }; const userScreens = { Home: HomeScreen, Profile: ProfileScreen, }; <Stack.Navigator> {Object.entries({ ...commonScreens, ...(isLoggedIn ? userScreens : authScreens), }).map(([name, component]) => ( <Stack.Screen name={name} component={component} /> ))} </Stack.Navigator>;
像 isLoggedIn
这样的全局变量动态刷新页面使用 Redux
架构实现起来会简单一些,具体实现可以参考 这篇教程
。