首發於公眾號 前端混合開發,歡迎關注。
移動應用程式由多個螢幕組成。在構建移動應用程式時,首要考慮的是如何處理使用者在應用程式中的導航問題,例如螢幕的展示和螢幕之間的切換。
React Navigation 是 React Native 最著名的導航庫之一。在本教程中,我們將探討 React Native 中導航的基礎知識,介紹如何開始使用 React Navigation,並透過一些 React Native 導航示例進行講解。
什麼是 React Navigation
React Navigation 是一個獨立的庫,可幫助我們在 React 應用程式中實現導航功能。
React Navigation 是用 JavaScript 編寫的,並不直接使用 iOS 和 Android 上的原生導航 API。相反,它重新建立了這些 API 的某些子集。這樣就可以整合第三方 JS 外掛,實現最大程度的自定義,並且更易於除錯,而無需學習 Objective-C
、Swift
、Java
、Kotlin
等語言。
什麼是 React Native Navigation
React Native Navigation 是一個受歡迎的 React Navigation 替代方案。它是一個依賴於並且被設計用於與 React Native 一起使用的模組。React Native Navigation 有一點不同,它直接使用 iOS 和 Android 上的原生導航 API,這使得它能夠提供更加原生的外觀和感覺。在撰寫本文時,React Native Navigation 的當前穩定版本是 React Navigation 6.1。
另一種選擇:React Router Native
React Router Native 是在 React Native 應用程式中實現導航功能的另一種解決方案。它由 Remix 團隊開發。
React Router Native 與 React Router 框架共享大部分 API 程式碼。這意味著,使用過 React Router 的 Web 開發人員會發現,使用 React Router Native 也很容易。
就易用性而言,React Navigation 和 React Router Native 完全相同。例如,請看下面的 Router Native 程式碼:
const Home = () => <Text>Home</Text>;
const About = () => <Text>About</Text>;
const App = () => (
<NativeRouter>
<View>
<View>
{/* Define our links. They are like anchor tags */}
<Link to="/">
<Text>Home</Text>
</Link>
<Link to="/about">
<Text>About</Text>
</Link>
</View>
{/*Define our routes for this project*/}
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
</View>
{/*The NativeRouter*/}
</NativeRouter>
);
與 Navigation 相比,我們可以看到程式碼是相似的:
import { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
function HomeScreen() {
return (
<View>
<Text>Home Screen</Text>
</View>
);
}
function AboutScreen() {
return (
<View>
<Text>About Screen</Text>
</View>
);
}
const Stack = createNativeStackNavigator();
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
{/*Define our routes*/}
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="About" component={AboutScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
兩個庫中用於實現路由的程式碼完全相同。這是一大優點,因為這意味著學習這兩個框架的難度都不大。
如果你來自Web 開發背景,我會推薦使用 React Router Native,因為它的使用方式與 React Router 相同。否則,應該選擇 React Navigation,因為它擁有更大的社群,因此有更多的開發支援。
安裝 React Navigation
既然我們已經瞭解了什麼是 React Navigation 以及它與 React Router Native 的關係,那麼就讓我們看看如何在應用程式中使用它。
注:在本文中,我們將在 React Native 應用程式中使用 Expo。
首先,我們使用下面的命令建立一個新的應用程式:
npx create-expo-app ReactNavigationDemo
這將建立一個名為 ReactNavigationDemo
的新專案:
接下來, cd 進入專案資料夾,開啟程式碼編輯器:
cd ReactNavigationDemo
如果使用的是 VS Code,則可以使用此功能在編輯器中開啟當前資料夾:
code .
用這個啟動應用程式:
npx expo start
接下來,使用以下任一命令在 React Native 專案中安裝 React Navigation 庫:
/* npm */
npm install @react-navigation/native
/* yarn */
yarn add @react-navigation/native
我們還需要安裝一些依賴項,即 react-native-screens
和 react-native-safe-area-context
:
如果你注意到了,我們沒有使用 npm
或 yarn
安裝這些依賴項。相反,我們使用了 npx expo install
,因為它會安裝與我們的專案軟體包相容的依賴版本。
我建議您始終使用該命令來安裝依賴包,因為 npm 和 yarn 將始終安裝最新版本,而最新版本可能與您的專案不相容。缺點是可能會出現生產級別的錯誤。
React Native 堆疊導航器
React Navigation 使用 JavaScript 構建,讓我們建立的元件和導航模式在外觀和感覺上都與真正的原生模式無異。
React Navigation 提供了一個基於堆疊的導航模型,允許螢幕被推入和彈出導航堆疊。透過堆疊導航,你可以使用一個堆疊導航器來定義你的應用程式的導航結構,該導航器維護著一個螢幕堆疊。任何給定時間只有一個螢幕呈現給使用者,每個螢幕在被推入堆疊時顯示,即當使用者導航到某個螢幕時,它就被推到堆疊的頂部。
想象一堆紙張。導航到一個新螢幕會將其放在堆疊的頂部,而導航回去則會將其從堆疊中移除。堆疊導航器還提供了類似於原生 iOS 和 Android 的過渡效果和手勢。
注意,一個應用程式可以有多個堆疊導航器。
理解堆疊導航器與原生堆疊導航器的區別
在 React Native 中,我們有兩個堆疊導航庫: @react-navigation/stack
和 @react-navigation/native-stack
。這兩個庫都提供了基於堆疊的導航模型,便於在螢幕之間進行轉換,將每個新螢幕放在堆疊的頂部。
然而,預設情況下,雖然 @react-navigation/stack
被配置為具有熟悉的 iOS 和 Android 外觀和感覺,並且可以自定義動畫,但 @react-navigation/native-stack
則利用了原生 API;iOS 上的 UINavigationController
和 Android 上的 Fragment,這樣導航的行為就會與原生構建的應用程式一樣。
為了理解這兩個庫之間的區別,讓我們從以下幾個關鍵因素來看看它們:
- 定製性:根據你的需求,
@react-navigation/native-stack
可能不如@react-navigation/stack
可定製。所以,如果你需要根據自己的感覺定製導航動畫,@react-navigation/stack
應該是你的首選。 - 效能:
@react-navigation/native-stack
提供了更好的效能。與@react-navigation/stack
相比,它使用本地堆疊 View 元件來渲染螢幕,使過渡動畫更快、更好、更流暢。 - 相容性:兩個庫都與 React Navigation 相容。
@react-navigation/native-stack
還透過react-native-web
提供了對Web的基本支援。另一方面,@react-navigation/stack
則沒有。 你還必須安裝react-native-gesture-handler
並在入口或根檔案(index.js 或 App.js)的頂部匯入它。跳過這一步通常會導致生產級別的崩潰,即使在開發中工作正常。此外,如果你想要使用 UIkit 風格來動畫化你的頭部,你將需要安裝一個額外的包:@react-native-masked-view/masked-view
。
React Native 導航器 React Native
在本節中,我們將探討 React Native 導航中的不同導航器,以及如何使用 React Navigation 庫實現它們。
1.使用堆疊導航器在螢幕元件之間導航
首先,我們建立兩個檔案,即 Homescreen
和 Aboutscreen
:
/* components/Homescreen.js */
import React from "react";
import { Button, View, Text } from "react-native";
export default function HomeScreen({ navigation }) {
return (
<View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
<Text>Home Screen</Text>
<Button
title="Go to About"
onPress={() => navigation.navigate("About")}
/>
</View>
);
}
請注意上面按鈕的 onPress
屬性 —— 我們稍後會解釋它的作用:
/* components/Aboutscreen.js */
import React, { Component } from "react";
import { Button, View, Text } from "react-native";
export default function AboutScreen() {
return (
<View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
<Text>About Screen</Text>
</View>
);
}
專案資料夾應該是這樣的:
我們對 App.js
也進行一些修改。 在這裡,我們必須進行以下匯入:
//tell React that we will implement a navigator
import { NavigationContainer } from "@react-navigation/native";
//create a stack navigator
import { createNativeStackNavigator } from "@react-navigation/native-stack";
在根 App.js 檔案中實現導航非常有用,因為從 App.js 匯出的元件是 React Native 應用程式的入口點(或根元件),而其他每個元件都是其後代。
正如你所看到的,我們將在導航功能中封裝所有其他元件:
/* App.js */
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import HomeScreen from './components/HomeScreen';
import AboutScreen from './components/AboutScreen';
const Stack = createNativeStackNavigator();
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
/>
<Stack.Screen
name="About"
component={AboutScreen}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
在上面的程式碼中, createNativeStackNavigator
為我們的應用程式提供了一種在螢幕之間過渡的方式,其中每個新螢幕都位於堆疊的頂部。我們將其配置為熟悉的 iOS 和 Android 外觀和感覺:在 iOS 中,新螢幕從右側滑入,而在 Android 中,新螢幕從底部淡入。
在這裡,我們執行了 createNativeStackNavigator
函式,並將其例項儲存在 Stack 變數中。稍後,我們將使用 Stack.Screen
標記傳遞路由。 Home
路由對應於 HomeScreen
,而 About
路由對應於 AboutScreen
。
此外, Stack
會按順序顯示,例如, HomeScreen 預設首先顯示,因為它位於 Stack 的最上方。要覆蓋這一預設選項,可以指定初始路徑。請參閱下面的程式碼:
/* App.js */
// imports....
const Stack = createNativeStackNavigator();
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName='About'>
<Stack.Screen
name="Home"
component={HomeScreen}
/>
<Stack.Screen
name="About"
component={AboutScreen}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
在幕後,createStackNavigator
函式為 HomeScreen 和 AboutScreen 元件提供了一個 navigate
屬性。
這個屬性允許導航到指定的螢幕元件。這就是為什麼我們可以在 HomeScreen.js
上的一個按鈕上使用它,當按下時,會導致頁面跳轉到 AboutScreen,如下所示:
<Button title="Go to About" onPress={() => navigation.navigate("About")} />;
在 App.js 程式碼中,我們將元件封裝在 NavigationContainer
元件中,最終建立了一個應用程式容器。該元件管理導航樹幷包含導航狀態。
最後,執行應用程式
npx expo start
2.使用 TabNavigator
大多數移動應用程式都有一個以上的螢幕。在此類移動應用程式中,常見的導航方式是基於標籤的導航。React Navigation 有一個名為 createBottomTabNavigator
的元件可以幫助我們實現這一點。
在實現基於標籤的導航之前,使用以下任一命令安裝 bottom-tabs
模組:
/* npm */
npm install @react-navigation/bottom-tabs
/* yarn */
yarn add @react-navigation/bottom-tabs
我們建立一個 ContactScreen
檔案,在應用程式中新增另一個螢幕:
/* components/ContactScreen.js */
import React, { Component } from "react";
import { Button, View, Text } from "react-native";
export default function ContactScreen() {
return (
<View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
<Text>Contact Screen</Text>
</View>
);
}
接下來,我們將其匯入 App.js
檔案:
import ContactScreen from './components/ContactScreen';
我們還將在我們的 App.js
檔案中實現我們的標籤導航。這是因為建議我們在根檔案中實現所有的導航配置,因為這些配置包裹了所有的導航結構,並將我們的螢幕作為子元素渲染。
我們用這行程式碼替換 createNativeStackNavigator
行:
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
我們的堆疊導航器也將發生變化。 App.js 應該是這樣的:
/* App.js */
import { NavigationContainer } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import ContactScreen from './components/ContactScreen';
import HomeScreen from './components/HomeScreen';
import AboutScreen from './components/AboutScreen';
const Tab = createBottomTabNavigator();
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator initialRouteName="Home">
<Tab.Screen
name="Home"
component={HomeScreen}
/>
<Tab.Screen
name="About"
component={AboutScreen}
/>
<Tab.Screen
name="Contact"
component={ContactScreen}
/>
</Tab.Navigator>
</NavigationContainer>
);
}
執行應用程式,它將看起來像這樣:
使用抽屜導航器
要使用抽屜導航,請首先使用以下任一命令安裝 @react-navigation/drawer
包:
接下來,我們將使用 npx expo install 安裝依賴項:
npx expo install react-native-gesture-handler react-native-reanimated
接下來,轉到 Reanimated 文件中設定專案中的手勢控制。完成這一步後,在你的 App.js 的頂部匯入手勢處理器包:
import "react-native-gesture-handler";
我們還要用抽屜導航更新導航器。複製並用下面的程式碼替換 App.js 程式碼:
/* App.js */
import 'react-native-gesture-handler';
import { NavigationContainer } from '@react-navigation/native';
import { createDrawerNavigator } from '@react-navigation/drawer';
import ContactScreen from './components/ContactScreen';
import HomeScreen from './components/HomeScreen';
import AboutScreen from './components/AboutScreen';
const Drawer = createDrawerNavigator();
export default function App() {
return (
<NavigationContainer>
<Drawer.Navigator initialRouteName="Home">
<Drawer.Screen
name="Home"
component={HomeScreen}
/>
<Drawer.Screen
name="About"
component={AboutScreen}
/>
<Drawer.Screen
name="Contact"
component={ContactScreen}
/>
</Drawer.Navigator>
</NavigationContainer>
);
}
執行應用程式檢視結果:
你可以透過在路由名稱旁邊新增圖示來自定義你的抽屜導航。我們的圖示放置在我們的資原始檔夾內:
我們可以透過在以下螢幕元件檔案中新增 navigationOptions
來進行自定義:
<NavigationContainer>
<Drawer.Navigator initialRouteName="Home">
<Drawer.Screen
name="Home"
component={HomeScreen}
options={{ //change the configuration of our screen
drawerIcon: ({ color, number, focused }) => { //set the icon:
return ( //the icon will be an image
<Image
source={require("../assets/home-icon.png")}
style={{ height: 30, width: 30 }}
/>
);
},
}}
/>
<Drawer.Screen
name="About"
component={AboutScreen}
options={{
drawerIcon: ({ color, number, focused }) => { //set the icon for all screens
return (
<Image
source={require("../assets/about-icon.png")}
style={{ height: 30, width: 30 }}
/>
);
},
}}
/>
<Drawer.Screen
name="Contact"
component={ContactScreen}
options={{
drawerIcon: ({ color, number, focused }) => {
return (
<Image
source={require("../assets/contact-icon.png")}
style={{ height: 30, width: 30 }}
/>
);
},
}}
/>
</Drawer.Navigator>
</NavigationContainer>
drawerActiveTintColor
屬性允許你根據導航標籤和標籤的啟用或非啟用狀態應用任何顏色。例如,我們可以更改我們導航抽屜標籤的啟用狀態顏色。轉到 Drawer.Navigator
變數,並新增到 options
物件中:
<Drawer.Navigator
initialRouteName="Home"
screenOptions={{ drawerActiveTintColor: "#e91e63" }}
>
//... further code.
這就導致了顏色的變化:
4.使用 useNavigation() 鉤子
React Navigation 還提供了一個名為 useNavigation
的 Hook。這個 Hook 使函式元件能夠訪問導航物件,並允許它們以程式設計方式觸發導航操作。當你無法直接將導航屬性傳遞給元件時,它非常有用。
老實說,我更經常使用 Hook,因為它更容易在我的功能元件中進行管理,而且使用起來也非常方便。
在你的 HomeScreen
檔案中,用下面的程式碼替換你的程式碼:
/* components/HomeScreen.js */
import React from 'react';
import { Button, View, Text } from 'react-native';
import { useNavigation } from '@react-navigation/native';
export default function HomeScreen() {
const navigation = useNavigation();
return (
<View
style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}
>
<Text>Home Screen</Text>
<Button
title="Go to About"
onPress={() => navigation.navigate('About')}
/>
</View>
);
}
useNavigation
鉤子是從 @react-navigation/native
模組匯入的,它會返回一個帶有程式設計操作的導航物件。
在 About 頁面中,可以為返回按鈕實現相同的方法。請參閱以下程式碼:
/* components/About.js */
import React, { Component } from 'react';
import { Button, View, Text } from 'react-native';
import { useNavigation } from '@react-navigation/native';
export default function AboutScreen() {
const navigation = useNavigation();
return (
<View
style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}
>
<Text>About Screen</Text>
<Button
title="Go Back"
onPress={() => {
navigation.goBack();
}}
/>
</View>
);
}
在 React Navigation 中向螢幕傳遞引數
向路由傳遞引數有兩個簡單的步驟:傳遞引數,然後在子路由或螢幕中讀取引數。
首先,將引數作為 navigation.navigate
函式的第二個引數放入一個物件中,從而將引數傳遞給路由:
然後,讀取螢幕元件中的引數。引數可在 route.params
中找到:
export default function HomeScreen({ route, navigation }) {
// 'route' 變數為我們提供頁面資訊。
// 它還儲存引數及其值
const { paramName } = route.params; // 我們的引數 'paramName' 儲存在這裡。
// ..後續程式碼..
}
最後,要設定頭部標題,我們可以像這樣使用 options
屬性的 title
屬性:
<Drawer.Screen
name="Home"
component={HomeScreen}
options={{
title: "Home Page", //set the title of the page to 'Home page'
}}
/>
總結
還有更多可以做的事情,而 React Navigation 將滿足你的大部分需求。要了解更多資訊,請檢視 React Navigation 文件,並隨時從我的 GitHub 倉庫中獲取最終程式碼。
交流
首發於公眾號 大遷世界,歡迎關注。📝 每週一篇實用的前端文章 🛠️ 分享值得關注的開發工具 ❓ 有疑問?我來回答
本文 GitHub https://github.com/qq449245884/xiaozhi 已收錄,有一線大廠面試完整考點、資料以及我的系列文章。