前言 在App中,底部TabBar
导航和顶部的导航栏导航是最常见的页面导航方式,而React Native 官方推荐的第三方库是@react-navigation
,正好今年React Navigation
发布了5.0版本,与前面的版本差别还挺大,不过使用上更方便了。本文主要讲解React Navigation
5.0及以上版本的使用。
安装 安装核心包
1 2 3 4 # NPM npm install @react-navigation/native # Yarn yarn add @react-navigation/native
安装依赖
1 2 3 4 5 # 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 Native 0.60及以上版本是自动链接库的,不需要手动运行react-native link
,但如果你用React Native 开发的是iOS,还需要手动安装pods
来完成库的链接:
把下面这行代码放在入口文件的顶部,比如index.js
或者App.js
1 import 'react-native-gesture-handler';
最后需要用NavigationContainer
将整个App包裹起来,这个代码一般也是放在入口文件,类似下面这样:
1 2 3 4 5 6 7 8 9 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
包裹在次外层,类似下面这样:
1 2 3 4 5 6 7 8 9 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
。
顶部导航栏导航 首先需要安装相应的库:
1 2 3 4 #NPM npm install @react-navigation/stack #Yarn yarn add @react-navigation/stack
Stack Navigation
使用上相对来说比较简单,只需要把需要导航的页面组件封装成Stack.Screen
,然后包裹在Stack.Navigator
就可以了,最上面的Stack.Screen
就是默认页面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 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
参数的,一种是直接设置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 /* 带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
属性,否则会报警告。
在需要跳转到其他页面时只需要以下代码就可以了,后面跟的是参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 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
方式创建的页面,代码会像下面这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 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
则会直接创建一个新的路由并跳转,也就是说可以重复多次跳转同一个页面。
堆栈导航的返回方式如下:
1 2 3 4 // 返回到上一页面 navigation.goBack() // 返回到堆栈里第一个页面 navigation.popToPop()
底部导航栏导航 除了顶部导航栏,最常用的就是底部导航栏了,首先也需要安装相应的库:
1 2 3 4 # NPM npm install @react-navigation/bottom-tabs # Yarn yarn add @react-navigation/bottom-tabs
底部导航栏的使用方法类似,需要将这些页面组件包装成Tab.Screen
,导航则由框架内部完成,不需要手动控制:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 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> ); }
底部导航栏属性可以按如下方式设置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <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
一同放到同一个堆栈中,但官方不推荐这种方式,嵌套层次太多会导致维护起来特别麻烦。官方推荐使用一个变量控制当前显示的页面,把不相关的模块隔离在不同的堆栈中,类似下面这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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 架构实现起来会简单一些,具体实现可以参考这篇教程 。