添加导航
在本章中,了解如何向 Expo 应用添加导航。
在本章中,我们将学习 Expo Router 的基础知识,以创建堆栈导航和带有两个选项卡的底部选项卡栏。
¥In this chapter, we'll learn Expo Router's fundamentals to create stack navigation and a bottom tab bar with two tabs.

Expo 路由基础知识
¥Expo Router basics
Expo Router 是一个基于文件的路由框架,适用于 React Native 和 Web 应用。它管理屏幕之间的导航,并在多个平台上使用相同的组件。要开始,我们需要了解以下约定:
¥Expo Router is a file-based routing framework for React Native and web apps. It manages navigation between screens and uses the same components across multiple platforms. To get started, we need to know about the following conventions:
-
app 目录:仅包含路由及其布局的特殊目录。添加到此目录的任何文件都将成为我们原生应用内的屏幕和 Web 上的页面。
¥app directory: A special directory containing only routes and their layouts. Any files added to this directory become a screen inside our native app and a page on the web.
-
根布局:app/_layout.tsx 文件。它定义共享的 UI 元素(例如标题和标签栏),以便它们在不同路由之间保持一致。
¥Root layout: The app/_layout.tsx file. It defines shared UI elements such as headers and tab bars so they are consistent between different routes.
-
文件命名约定:索引文件名(例如 index.tsx)与其父目录匹配,并且不添加路径段。例如,应用目录中的 index.tsx 文件与
/
路由匹配。¥File name conventions: Index file names, such as index.tsx, match their parent directory and do not add a path segment. For example, the index.tsx file in the app directory matches
/
route. -
路由文件将 React 组件导出为其默认值。它可以使用
.js
、.jsx
、.ts
或.tsx
扩展。¥A route file exports a React component as its default value. It can use either
.js
,.jsx
,.ts
, or.tsx
extension. -
Android、iOS 和 Web 共享统一的导航结构。
¥Android, iOS, and web share a unified navigation structure.
上述列表足以让我们开始。有关功能的完整列表,请参阅 Expo 路由简介。
¥The above list is enough for us to get started. For a complete list of features, see Introduction to Expo Router.
1
向堆栈添加新屏幕
¥Add a new screen to the stack
让我们在 app 目录中创建一个名为 about.tsx 的新文件。当用户导航到 /about
路由时,它会显示屏幕名称。
¥Let's create a new file named about.tsx inside the app directory. It displays the screen name when the user navigates to the /about
route.
import { Text, View, StyleSheet } from 'react-native';
export default function AboutScreen() {
return (
<View style={styles.container}>
<Text style={styles.text}>About screen</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#25292e',
justifyContent: 'center',
alignItems: 'center',
},
text: {
color: '#fff',
},
});
在 _layout.tsx 中:
¥Inside _layout.tsx:
-
添加
<Stack.Screen />
组件和options
属性,以更新/about
路由的标题。¥Add a
<Stack.Screen />
component and anoptions
prop to update the title of the/about
route. -
通过添加
options
prop 将/index
路由的标题更新为Home
。¥Update the
/index
route's title toHome
by addingoptions
prop.
import { Stack } from 'expo-router';
export default function RootLayout() {
return (
<Stack>
<Stack.Screen name="index" options={{ title: 'Home' }} />
<Stack.Screen name="about" options={{ title: 'About' }} />
</Stack>
);
}
What is a Stack
?
堆栈导航器是在应用中的不同屏幕之间导航的基础。在 Android 上,堆叠路由会在当前屏幕顶部动画。在 iOS 上,堆叠路由从右侧动画显示。Expo Router 提供了一个 Stack
组件来创建导航堆栈以添加新路由。
¥A stack navigator is the foundation for navigating between different screens in an app. On Android, a stacked route animates on top of the current screen. On iOS, a stacked route animates from the right. Expo Router provides a Stack
component to create a navigation stack to add new routes.
2
导航屏幕之间
¥Navigate between screens
我们将使用 Expo Router 的 Link
组件从 /index
路由导航到 /about
路由。它是一个 React 组件,使用给定的 href
prop 渲染 <Text>
。
¥We'll use Expo Router's Link
component to navigate from the /index
route to the /about
route. It is a React component that renders a <Text>
with a given href
prop.
-
在 index.tsx 中从
expo-router
导入Link
组件。¥Import the
Link
component fromexpo-router
inside index.tsx. -
在
<Text>
组件后添加Link
组件,并通过/about
路由传递href
属性。¥Add a
Link
component after<Text>
component and passhref
prop with the/about
route. -
将
fontSize
、textDecorationLine
和color
的样式添加到Link
组件。它采用与<Text>
组件相同的属性。¥Add a style of
fontSize
,textDecorationLine
, andcolor
toLink
component. It takes the same props as the<Text>
component.
import { Text, View, StyleSheet } from 'react-native';
import { Link } from 'expo-router';
export default function Index() {
return (
<View style={styles.container}>
<Text style={styles.text}>Home screen</Text>
<Link href="/about" style={styles.button}>
Go to About screen
</Link>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#25292e',
alignItems: 'center',
justifyContent: 'center',
},
text: {
color: '#fff',
},
button: {
fontSize: 20,
textDecorationLine: 'underline',
color: '#fff',
},
});
让我们看看我们应用中的变化。单击 Link
导航到 /about
路由:
¥Let's took a look at the changes in our app. Click on Link
to navigate to the /about
route:
3
添加未找到的路由
¥Add a not-found route
当路由不存在时,我们可以使用 +not-found
路由显示后备屏幕。当我们想要在移动设备上导航到无效路由时显示自定义屏幕,而不是使应用崩溃或在 Web 上显示 404 错误时,这很有用。Expo Router 使用特殊的 +not-found.tsx 文件来处理这种情况。
¥When a route doesn't exist, we can use a +not-found
route to display a fallback screen. This is useful when we want to display a custom screen when navigating to an invalid route on mobile instead of crashing the app or display a 404 error on web. Expo Router uses a special +not-found.tsx file to handle this case.
-
在应用目录中创建一个名为 +not-found.tsx 的新文件以添加
NotFoundScreen
组件。¥Create a new file named +not-found.tsx inside the app directory to add the
NotFoundScreen
component. -
从
Stack.Screen
添加options
属性,以显示此路由的自定义屏幕标题。¥Add
options
prop from theStack.Screen
to display a custom screen title for this route. -
添加
Link
组件以导航到/
路由,这是我们的后备路由。¥Add a
Link
component to navigate to the/
route, which is our fallback route.
import { View, StyleSheet } from 'react-native';
import { Link, Stack } from 'expo-router';
export default function NotFoundScreen() {
return (
<>
<Stack.Screen options={{ title: 'Oops! Not Found' }} />
<View style={styles.container}>
<Link href="/" style={styles.button}>
Go back to Home screen!
</Link>
</View>
</>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#25292e',
justifyContent: 'center',
alignItems: 'center',
},
button: {
fontSize: 20,
textDecorationLine: 'underline',
color: '#fff',
},
});
要测试这一点,请在 Web 浏览器中导航到 http:localhost:8081/123
URL,因为在那里更改 URL 路径很容易。应用应显示 NotFoundScreen
组件:
¥To test this, navigate to http:localhost:8081/123
URL in the web browser since it is easy to change the URL path there. The app should display the NotFoundScreen
component:
4
添加底部选项卡导航器
¥Add a bottom tab navigator
此时,我们的应用目录的文件结构如下所示:
¥At this point, the file structure of our app directory looks like the following:
app
_layout.tsx
Root layout
index.tsx
matches route '/'
about.tsx
matches route '/about'
+not-found.tsx
matches route any 404 route
我们将向我们的应用添加一个底部选项卡导航器,并重用现有的主页和关于屏幕来创建选项卡布局(许多社交媒体应用(如 X 或 BlueSky)中的常见导航模式)。我们还将在 Root 布局中使用堆栈导航器,以便 +not-found
路由显示在任何其他嵌套导航器上。
¥We'll add a bottom tab navigator to our app and reuse the existing Home and About screens to create a tab layout (a common navigation pattern in many social media apps like X or BlueSky). We'll also use the stack navigator in the Root layout so the +not-found
route displays over any other nested navigators.
-
在 app 目录中,添加一个 (tabs) 子目录。此特殊目录用于将路由分组在一起并将它们显示在底部选项卡栏中。
¥Inside the app directory, add a (tabs) subdirectory. This special directory is used to group routes together and display them in a bottom tab bar.
-
在目录中创建一个 (tabs)/_layout.tsx 文件。它将用于定义选项卡布局,该布局与 Root 布局分开。
¥Create a (tabs)/_layout.tsx file inside the directory. It will be used to define the tab layout, which is separate from Root layout.
-
将现有的 index.tsx 和 about.tsx 文件移动到 (tabs) 目录内。应用目录的结构将如下所示:
¥Move the existing index.tsx and about.tsx files inside the (tabs) directory. The structure of app directory will look like this:
app
_layout.tsx
Root layout
+not-found.tsx
matches route any 404 route
(tabs)
_layout.tsx
Tab layout
index.tsx
matches route '/'
about.tsx
matches route '/about'
更新根布局文件以添加 (tabs)
路由:
¥Update the Root layout file to add a (tabs)
route:
import { Stack } from 'expo-router';
export default function RootLayout() {
return (
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen name="+not-found" />
</Stack>
);
}
在 (tabs)/_layout.tsx 中,添加一个 Tabs
组件来定义底部选项卡布局:
¥Inside (tabs)/_layout.tsx, add a Tabs
component to define the bottom tab layout:
import { Tabs } from 'expo-router';
export default function TabLayout() {
return (
<Tabs>
<Tabs.Screen name="index" options={{ title: 'Home' }} />
<Tabs.Screen name="about" options={{ title: 'About' }} />
</Tabs>
);
}
现在让我们看一下我们的应用,看看新的底部选项卡:
¥Let's take a look at our app now to see the new bottom tabs:
5
更新 gradle 变量
¥Update bottom tab navigator appearance
目前,底部选项卡导航器在所有平台上看起来都一样,但与我们的应用风格不匹配。例如,标签栏或标题不显示自定义图标,底部标签背景颜色与应用的背景颜色不匹配。
¥Right now, the bottom tab navigator looks the same on all platforms but doesn't match the style of our app. For example, the tab bar or header doesn't display a custom icon, and the bottom tab background color doesn't match the app's background color.
修改 (tabs)/_layout.tsx 文件以添加标签栏图标:
¥Modify the (tabs)/_layout.tsx file to add tab bar icons:
-
从
@expo/vector-icons
导入Ionicons
图标集 — 一个包含流行图标集的库。¥Import
Ionicons
icons set from@expo/vector-icons
— a library that includes popular icon sets. -
将
tabBarIcon
添加到index
和about
路由中。此函数将focused
和color
作为参数并渲染图标组件。从图标集中,我们可以提供自定义图标名称。¥Add the
tabBarIcon
to both theindex
andabout
routes. This function takesfocused
andcolor
as params and renders the icon component. From the icon set, we can provide custom icon names. -
将
screenOptions.tabBarActiveTintColor
添加到Tabs
组件并将其值设置为#ffd33d
。这将在活动时更改标签栏图标和标签的颜色。¥Add
screenOptions.tabBarActiveTintColor
to theTabs
component and set its value to#ffd33d
. This will change the color of the tab bar icon and label when active.
import { Tabs } from 'expo-router';
import Ionicons from '@expo/vector-icons/Ionicons';
export default function TabLayout() {
return (
<Tabs
screenOptions={{
tabBarActiveTintColor: '#ffd33d',
}}
>
<Tabs.Screen
name="index"
options={{
title: 'Home',
tabBarIcon: ({ color, focused }) => (
<Ionicons name={focused ? 'home-sharp' : 'home-outline'} color={color} size={24} />
),
}}
/>
<Tabs.Screen
name="about"
options={{
title: 'About',
tabBarIcon: ({ color, focused }) => (
<Ionicons name={focused ? 'information-circle' : 'information-circle-outline'} color={color} size={24}/>
),
}}
/>
</Tabs>
);
}
让我们使用 screenOptions
prop 更改标签栏和标题的背景颜色:
¥Let's also change the background color of the tab bar and header using screenOptions
prop:
<Tabs
screenOptions={{
tabBarActiveTintColor: '#ffd33d',
headerStyle: {
backgroundColor: '#25292e',
},
headerShadowVisible: false,
headerTintColor: '#fff',
tabBarStyle: {
backgroundColor: '#25292e',
},
}}
>
在上面的代码中:
¥In the above code:
-
使用
headerStyle
属性将标题的背景设置为#25292e
。我们还使用headerShadowVisible
禁用了标题的阴影。¥The header's background is set to
#25292e
using theheaderStyle
property. We have also disabled the header's shadow usingheaderShadowVisible
. -
headerTintColor
将#fff
应用于标题标签¥
headerTintColor
applies#fff
to the header label -
tabBarStyle.backgroundColor
将#25292e
应用于标签栏¥
tabBarStyle.backgroundColor
applies#25292e
to the tab bar
我们的应用现在有一个自定义底部标签导航器:
¥Our app now has a custom bottom tabs navigator:
概括
¥Summary
Chapter 2: Add navigation
We've successfully added a stack and a tab navigator to our app.
In the next chapter, we'll learn how to build the app's first screen.