【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
架構實現起來會簡單一些,具體實現可以參考 這篇教程
。