【React Native娓娓道来】第5课 导航与多页面:用 React Navigation 组织页面流
开篇:从"一个屏幕"到"一组屏幕"
前面四课,所有代码都写在一个组件里。状态、副作用、列表、组件组合,都在同一个屏幕内完成。
但真实的 App 不可能只有一个页面。电商应用有首页、商品列表、商品详情、购物车、个人中心;社交应用有动态流、消息列表、聊天窗口、个人主页。用户需要在多个页面之间跳转、返回、传递数据。
这引出一个新问题:
如何管理多个页面,以及页面之间的跳转?
这就是导航要解决的问题。
React Native 没有内置导航功能。社区中最主流的方案是 React Navigation。它提供了路由逻辑和移动端常见的 UI 模式,支持 iOS、Android 和 Web。
这一课围绕三种导航器和一条主线展开:
// 概念表达
Stack -> 一摞卡片,有去有回 // 顺序流程
Tab -> 一排标签,平级切换 // 模块切换
Drawer -> 侧边菜单,全局入口 // 全局导航
一、导航的基本概念
1.1 移动端的三种导航模式
Web 用链接和 URL 切换页面,移动端则发展出自己的模式:
- 堆栈式:像一摞卡片,新页面盖在旧页面上,返回时抽走最上面那张;
- 标签式:底部或顶部有一排标签,点击切换不同模块;
- 抽屉式:从屏幕边缘滑出一个菜单,选择要去的页面。
React Navigation 为每种模式提供了对应的导航器。理解这三种模式,就理解了移动端导航的骨架。
1.2 安装
在 Expo 项目中安装 React Navigation 核心包:
# Bash 终端命令
npx expo install @react-navigation/native react-native-screens react-native-safe-area-context # 核心包和依赖
再安装需要的导航器:
# Bash 终端命令
npx expo install @react-navigation/native-stack # 堆栈导航器
npx expo install @react-navigation/bottom-tabs # 底部标签导航器
npx expo install @react-navigation/drawer react-native-gesture-handler react-native-reanimated # 抽屉导航器
@react-navigation/native 是核心包,react-native-screens 和 react-native-safe-area-context 是大多数导航器需要的依赖。用到哪个导航器,就装哪个。
1.3 基本结构
React Navigation 的核心结构:
// JavaScript + React Native
import { NavigationContainer } from '@react-navigation/native'; // 引入导航容器
import { createNativeStackNavigator } from '@react-navigation/native-stack'; // 引入堆栈导航器创建函数
const Stack = createNativeStackNavigator(); // 创建堆栈导航器对象
export default function App() { // 定义并默认导出 App
return ( // 返回界面描述
<NavigationContainer> // 导航容器,管理导航状态
<Stack.Navigator> // 堆栈导航器,定义页面集合
<Stack.Screen name="Home" component={HomeScreen} /> // 注册 Home 页面
<Stack.Screen name="Details" component={DetailsScreen} /> // 注册 Details 页面
</Stack.Navigator> // 结束导航器
</NavigationContainer> // 结束容器
); // 结束 return
} // 结束 App
三个关键部分,可以这样理解:
NavigationContainer:总控室,管理导航状态;Stack.Navigator:路线图,定义有哪些页面以及它们的关系;Stack.Screen:路线上的一个站点,name是路由名称,component是页面组件。
你只需要声明有哪些页面,不需要指挥怎么切换。 这就是"视图 = 函数(状态)"在导航层面的体现——导航状态变了,导航器自动显示对应页面。
二、Stack Navigator:堆栈导航
2.1 什么是堆栈导航
Stack Navigator 模拟"一摞卡片":
- 打开新页面时,新页面从右侧滑入,盖在旧页面上;
- 按返回键或返回按钮时,最上面的页面滑出,回到上一个页面;
- 页面按打开顺序排列在堆栈中。
适合"列表 → 详情 → 详情详情"这样的顺序流程。
2.2 创建第一个堆栈导航
// JavaScript + React Native
import { NavigationContainer } from '@react-navigation/native'; // 引入导航容器
import { createNativeStackNavigator } from '@react-navigation/native-stack'; // 引入堆栈导航器
import { View, Text, Button, StyleSheet } from 'react-native'; // 引入需要的组件
function HomeScreen({ navigation }) { // 定义 Home 页面,接收 navigation
return ( // 返回界面描述
<View style={styles.center}> // 居中容器
<Text style={styles.title}>首页</Text> // 页面标题
<Button // 按钮
title="查看详情" // 按钮文字
onPress={() => navigation.navigate('Details')} // 点击跳转到 Details
/> // 结束 Button
</View> // 结束容器
); // 结束 return
} // 结束 HomeScreen
function DetailsScreen({ navigation }) { // 定义 Details 页面,接收 navigation
return ( // 返回界面描述
<View style={styles.center}> // 居中容器
<Text style={styles.title}>详情页</Text> // 页面标题
<Button // 按钮
title="返回首页" // 按钮文字
onPress={() => navigation.goBack()} // 点击返回上一页
/> // 结束 Button
</View> // 结束容器
); // 结束 return
} // 结束 DetailsScreen
const Stack = createNativeStackNavigator(); // 创建堆栈导航器对象
export default function App() { // 定义并默认导出 App
return ( // 返回界面描述
<NavigationContainer> // 导航容器
<Stack.Navigator> // 堆栈导航器
<Stack.Screen name="Home" component={HomeScreen} /> // 注册 Home 页面
<Stack.Screen name="Details" component={DetailsScreen} /> // 注册 Details 页面
</Stack.Navigator> // 结束导航器
</NavigationContainer> // 结束容器
); // 结束 return
} // 结束 App
const styles = StyleSheet.create({ // 创建样式表
center: { flex: 1, justifyContent: 'center', alignItems: 'center' }, // 居中容器样式
title: { fontSize: 24, fontWeight: 'bold', marginBottom: 16 }, // 标题样式
}); // 结束样式表
每个页面组件会自动收到一个 navigation 对象,包含导航方法。
2.3 导航方法对比
| 方法 | 行为 | 什么时候用 |
|---|---|---|
navigate('Name') | 已经在 Name 页面则更新参数;否则压入堆栈 | 日常跳转,最常用 |
push('Name') | 总是压入一个新页面 | 同一页面需要重复打开(比如看不同商品) |
goBack() | 返回上一页 | 用户主动返回 |
popToTop() | 回到堆栈最底部 | 回到首页 |
2.4 配置页面标题
// JavaScript + React Native (JSX)
<Stack.Navigator screenOptions={{ headerStyle: { backgroundColor: '#4A90D9' }, headerTintColor: '#fff' }}> // 统一配置头部样式
<Stack.Screen // Home 页面
name="Home" // 路由名称
component={HomeScreen} // 页面组件
options={{ title: '首页' }} // 单独配置标题
/> // 结束 Home
<Stack.Screen // Details 页面
name="Details" // 路由名称
component={DetailsScreen} // 页面组件
options={{ title: '详情' }} // 单独配置标题
/> // 结束 Details
</Stack.Navigator> // 结束导航器
screenOptions 作用于所有页面,options 只作用于当前页面。两者同时存在时,options 优先级更高。
三、页面间参数传递
3.1 传递参数
导航时携带数据:
// JavaScript + React Native
navigation.navigate('Details', { // 跳转到 Details
itemId: 86, // 传递 itemId
title: 'React Native 入门', // 传递 title
}); // 结束 navigate
第二个参数就是传递给目标页面的数据。
3.2 读取参数
目标页面通过 route.params 读取:
// JavaScript + React Native
function DetailsScreen({ route }) { // 定义详情页面,接收 route
const { itemId, title } = route.params; // 从 route.params 中解构参数
return ( // 返回界面描述
<View> // 外层容器
<Text>ID: {itemId}</Text> // 显示 ID
<Text>标题: {title}</Text> // 显示标题
</View> // 结束容器
); // 结束 return
} // 结束 DetailsScreen
每个页面组件会自动收到 route 对象,route.params 就是导航时传入的参数。
3.3 完整示例:文章列表到详情
// JavaScript + React Native
import { NavigationContainer } from '@react-navigation/native'; // 引入导航容器
import { createNativeStackNavigator } from '@react-navigation/native-stack'; // 引入堆栈导航器
import { View, Text, Button, FlatList, Pressable, StyleSheet } from 'react-native'; // 引入需要的组件
function HomeScreen({ navigation }) { // 定义 Home 页面
const posts = [ // 文章列表数据
{ id: '1', title: 'React Native 入门' }, // 第一篇文章
{ id: '2', title: '组件与 Props' }, // 第二篇文章
{ id: '3', title: '列表与导航' }, // 第三篇文章
]; // 结束数组
return ( // 返回界面描述
<FlatList // 高效列表
data={posts} // 数据源
keyExtractor={(item) => item.id} // 从每项提取唯一 key
renderItem={({ item }) => ( // 如何渲染每一项
<Pressable // 可点击容器
style={styles.item} // 应用 item 样式
onPress={() => // 点击回调
navigation.navigate('Details', { // 跳转到 Details
itemId: item.id, // 传递 id
title: item.title, // 传递标题
}) // 结束 navigate
} // 结束回调
> // 开始 Pressable
<Text style={styles.itemText}>{item.title}</Text> // 显示文章标题
</Pressable> // 结束 Pressable
)} // 结束 renderItem
/> // 结束 FlatList
); // 结束 return
} // 结束 HomeScreen
function DetailsScreen({ route, navigation }) { // 定义 Details 页面
const { itemId, title } = route.params; // 从路由参数中解构
return ( // 返回界面描述
<View style={styles.center}> // 居中容器
<Text style={styles.title}>{title}</Text> // 显示文章标题
<Text>文章 ID: {itemId}</Text> // 显示文章 ID
<Button title="返回" onPress={() => navigation.goBack()} /> // 返回按钮
</View> // 结束容器
); // 结束 return
} // 结束 DetailsScreen
const Stack = createNativeStackNavigator(); // 创建堆栈导航器对象
export default function App() { // 定义并默认导出 App
return ( // 返回界面描述
<NavigationContainer> // 导航容器
<Stack.Navigator> // 堆栈导航器
<Stack.Screen name="Home" component={HomeScreen} /> // 注册 Home 页面
<Stack.Screen name="Details" component={DetailsScreen} /> // 注册 Details 页面
</Stack.Navigator> // 结束导航器
</NavigationContainer> // 结束容器
); // 结束 return
} // 结束 App
const styles = StyleSheet.create({ // 创建样式表
center: { flex: 1, justifyContent: 'center', alignItems: 'center' }, // 居中容器样式
title: { fontSize: 24, fontWeight: 'bold', marginBottom: 8 }, // 标题样式
item: { padding: 16, borderBottomWidth: 1, borderBottomColor: '#eee' }, // 列表项样式
itemText: { fontSize: 18 }, // 列表项文字样式
}); // 结束样式表
这就是"列表 → 详情"的标准流程:列表页通过 navigate 传递参数,详情页通过 route.params 读取。
3.4 参数的注意事项
传递的参数最好是可序列化的(字符串、数字、布尔值、简单对象)。避免传递函数、类实例等不可序列化的值,否则可能影响深度链接和状态持久化。
四、Bottom Tab Navigator:底部标签导航
4.1 什么是标签导航
底部标签导航在屏幕底部显示一排标签,点击切换不同模块。微信、微博、抖音的底部导航都是这种形式。
特点:
- 每个标签对应一个独立模块;
- 标签之间平级,没有"返回"的概念;
- 标签页面懒加载,首次访问时才挂载,之后保留在内存中。
4.2 基本用法
// JavaScript + React Native
import { NavigationContainer } from '@react-navigation/native'; // 引入导航容器
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; // 引入底部标签导航器
import { View, Text, StyleSheet } from 'react-native'; // 引入需要的组件
function HomeScreen() { // 定义 Home 页面
return <View style={styles.center}><Text>首页</Text></View>; // 返回居中文字
} // 结束 HomeScreen
function SearchScreen() { // 定义 Search 页面
return <View style={styles.center}><Text>搜索</Text></View>; // 返回居中文字
} // 结束 SearchScreen
function ProfileScreen() { // 定义 Profile 页面
return <View style={styles.center}><Text>个人中心</Text></View>; // 返回居中文字
} // 结束 ProfileScreen
const Tab = createBottomTabNavigator(); // 创建标签导航器对象
export default function App() { // 定义并默认导出 App
return ( // 返回界面描述
<NavigationContainer> // 导航容器
<Tab.Navigator> // 标签导航器
<Tab.Screen name="Home" component={HomeScreen} /> // 注册 Home 标签
<Tab.Screen name="Search" component={SearchScreen} /> // 注册 Search 标签
<Tab.Screen name="Profile" component={ProfileScreen} /> // 注册 Profile 标签
</Tab.Navigator> // 结束导航器
</NavigationContainer> // 结束容器
); // 结束 return
} // 结束 App
const styles = StyleSheet.create({ // 创建样式表
center: { flex: 1, justifyContent: 'center', alignItems: 'center' }, // 居中容器样式
}); // 结束样式表
4.3 自定义标签图标
// JavaScript + React Native (JSX)
<Tab.Navigator // 标签导航器
screenOptions={({ route }) => ({ // 统一配置
tabBarIcon: ({ color, size }) => { // 图标渲染函数
const icons = { Home: '🏠', Search: '🔍', Profile: '👤' }; // 图标映射表
return <Text style={{ fontSize: size, color }}>{icons[route.name]}</Text>; // 根据路由名返回图标
}, // 结束 tabBarIcon
})} // 结束 screenOptions
> // 结束开始标签
{/* 页面定义 */}
</Tab.Navigator> // 结束导航器
实际项目中通常使用 @expo/vector-icons 或 react-native-vector-icons 来显示图标。
4.4 标签导航与堆栈导航的配合
标签导航通常不单独使用。更常见的模式是:每个标签内部再嵌套一个堆栈导航。
为什么? 因为标签之间是平级的,但每个模块内部可能有自己的页面流程。比如"首页"标签内部需要从文章列表跳到文章详情,这就需要 Stack 来管理。
五、Drawer Navigator:抽屉导航
5.1 什么是抽屉导航
抽屉导航从屏幕边缘滑出一个侧边菜单,通常包含全局导航选项。在 Android 上通常通过边缘滑动打开,在 iOS 上也可以通过头部按钮打开。
5.2 基本用法
// JavaScript + React Native
import { NavigationContainer } from '@react-navigation/native'; // 引入导航容器
import { createDrawerNavigator } from '@react-navigation/drawer'; // 引入抽屉导航器
import { View, Text, StyleSheet } from 'react-native'; // 引入需要的组件
function HomeScreen() { // 定义 Home 页面
return <View style={styles.center}><Text>首页</Text></View>; // 返回居中文字
} // 结束 HomeScreen
function SettingsScreen() { // 定义 Settings 页面
return <View style={styles.center}><Text>设置</Text></View>; // 返回居中文字
} // 结束 SettingsScreen
const Drawer = createDrawerNavigator(); // 创建抽屉导航器对象
export default function App() { // 定义并默认导出 App
return ( // 返回界面描述
<NavigationContainer> // 导航容器
<Drawer.Navigator> // 抽屉导航器
<Drawer.Screen name="Home" component={HomeScreen} /> // 注册 Home 页面
<Drawer.Screen name="Settings" component={SettingsScreen} /> // 注册 Settings 页面
</Drawer.Navigator> // 结束导航器
</NavigationContainer> // 结束容器
); // 结束 return
} // 结束 App
const styles = StyleSheet.create({ // 创建样式表
center: { flex: 1, justifyContent: 'center', alignItems: 'center' }, // 居中容器样式
}); // 结束样式表
5.3 三种导航器的对比
| 导航器 | 适用场景 | 页面关系 | 类比 |
|---|---|---|---|
| Stack | 列表 → 详情 | 堆叠,有返回 | 一摞卡片 |
| Bottom Tab | 多个平级模块 | 平级切换 | 一排抽屉 |
| Drawer | 全局菜单 | 侧边滑出 | 一个侧柜 |
实际项目中,三种导航器通常嵌套使用:最外层是 Drawer 或 Tab,内部嵌套 Stack。
六、嵌套导航器
6.1 为什么要嵌套
真实 App 的导航结构很少只有一种导航器。常见的结构是:
- 最外层:Tab Navigator(首页、搜索、个人中心);
- 首页内部:Stack Navigator(首页列表 → 详情页);
- 个人中心内部:Stack Navigator(个人资料 → 设置)。
这就是嵌套导航器:一个导航器出现在另一个导航器的页面中。
6.2 嵌套示例
// JavaScript + React Native
import { NavigationContainer } from '@react-navigation/native'; // 引入导航容器
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; // 引入底部标签导航器
import { createNativeStackNavigator } from '@react-navigation/native-stack'; // 引入堆栈导航器
import { View, Text, Button, StyleSheet } from 'react-native'; // 引入需要的组件
const Tab = createBottomTabNavigator(); // 创建标签导航器对象
const Stack = createNativeStackNavigator(); // 创建堆栈导航器对象
function FeedScreen({ navigation }) { // 定义 Feed 列表页面
return ( // 返回界面描述
<View style={styles.center}> // 居中容器
<Text>动态列表</Text> // 页面文字
<Button // 按钮
title="查看动态详情" // 按钮文字
onPress={() => navigation.navigate('FeedDetails', { id: '1' })} // 跳转详情页
/> // 结束 Button
</View> // 结束容器
); // 结束 return
} // 结束 FeedScreen
function FeedDetailsScreen({ route }) { // 定义 Feed 详情页面
return ( // 返回界面描述
<View style={styles.center}> // 居中容器
<Text>动态详情 ID: {route.params.id}</Text> // 显示详情 ID
</View> // 结束容器
); // 结束 return
} // 结束 FeedDetailsScreen
function FeedStack() { // 定义 Feed 的堆栈导航器
return ( // 返回界面描述
<Stack.Navigator> // 堆栈导航器
<Stack.Screen name="FeedList" component={FeedScreen} options={{ title: '动态' }} /> // 列表页面
<Stack.Screen name="FeedDetails" component={FeedDetailsScreen} options={{ title: '详情' }} /> // 详情页面
</Stack.Navigator> // 结束导航器
); // 结束 return
} // 结束 FeedStack
function ProfileScreen() { // 定义 Profile 页面
return ( // 返回界面描述
<View style={styles.center}> // 居中容器
<Text>个人中心</Text> // 页面文字
</View> // 结束容器
); // 结束 return
} // 结束 ProfileScreen
export default function App() { // 定义并默认导出 App
return ( // 返回界面描述
<NavigationContainer> // 导航容器
<Tab.Navigator> // 标签导航器
<Tab.Screen name="FeedTab" component={FeedStack} options={{ title: '动态' }} /> // Feed 标签,内嵌堆栈
<Tab.Screen name="ProfileTab" component={ProfileScreen} options={{ title: '我的' }} /> // Profile 标签
</Tab.Navigator> // 结束导航器
</NavigationContainer> // 结束容器
); // 结束 return
} // 结束 App
const styles = StyleSheet.create({ // 创建样式表
center: { flex: 1, justifyContent: 'center', alignItems: 'center' }, // 居中容器样式
}); // 结束样式表
结构如下:
// 概念表达
Tab.Navigator // 外层标签导航器
├── FeedTab -> Stack.Navigator // Feed 标签内嵌堆栈导航器
│ ├── FeedList // 列表页面
│ └── FeedDetails // 详情页面
└── ProfileTab -> ProfileScreen // Profile 标签
在 Feed 标签内可以跳转到详情页,底部标签栏仍然显示。切换到 Profile 标签再切回来,Feed 的堆栈状态会保留。
6.3 跨导航器跳转
如果需要从一个导航器跳转到另一个导航器中的页面,可以嵌套 navigate:
// JavaScript + React Native
navigation.navigate('ProfileTab', { // 目标标签
screen: 'ProfileDetails', // 目标页面
params: { userId: '123' }, // 传递参数
}); // 结束 navigate
这会先切换到 ProfileTab,再在它的内部堆栈中打开 ProfileDetails 页面。
6.4 常见嵌套结构
// 概念表达
Tab.Navigator (底部标签)
├── 首页 -> Stack.Navigator (首页堆栈)
│ ├── HomeMain (首页)
│ └── HomeDetail (首页详情)
├── 发现 -> Stack.Navigator (发现堆栈)
│ ├── DiscoverMain (发现)
│ └── DiscoverDetail (发现详情)
└── 我的 -> Stack.Navigator (我的堆栈)
├── ProfileMain (个人主页)
└── Settings (设置)
每个标签内部有自己的页面流程,标签之间平级切换。这是 React Native App 的标准结构。
七、认证流程:状态驱动导航
7.1 什么是认证流程
很多 App 需要在登录后才能访问主要功能。React Navigation 推荐的做法是:根据登录状态,条件性地渲染不同的页面组。
注意关键词:条件渲染。这和"视图 = 函数(状态)"完全一致。
7.2 实现思路
// JavaScript + React Native
import { NavigationContainer } from '@react-navigation/native'; // 引入导航容器
import { createNativeStackNavigator } from '@react-navigation/native-stack'; // 引入堆栈导航器
import { useState } from 'react'; // 引入 useState
import { View, Text, Button, TextInput, StyleSheet } from 'react-native'; // 引入需要的组件
function SignInScreen({ onSignIn }) { // 定义登录页面
const [username, setUsername] = useState(''); // 创建状态 username,初始空字符串
return ( // 返回界面描述
<View style={styles.center}> // 居中容器
<Text style={styles.title}>登录</Text> // 标题
<TextInput // 输入框
style={styles.input} // 应用 input 样式
placeholder="用户名" // 占位提示
value={username} // 值由 username 决定
onChangeText={setUsername} // 输入变化时更新 username
/> // 结束 TextInput
<Button title="登录" onPress={() => onSignIn(username)} /> // 点击登录
</View> // 结束容器
); // 结束 return
} // 结束 SignInScreen
function HomeScreen({ onSignOut }) { // 定义主页面
return ( // 返回界面描述
<View style={styles.center}> // 居中容器
<Text style={styles.title}>欢迎回来</Text> // 欢迎文字
<Button title="退出登录" onPress={onSignOut} /> // 退出按钮
</View> // 结束容器
); // 结束 return
} // 结束 HomeScreen
const Stack = createNativeStackNavigator(); // 创建堆栈导航器对象
export default function App() { // 定义并默认导出 App
const [user, setUser] = useState(null); // 创建状态 user,初始 null
return ( // 返回界面描述
<NavigationContainer> // 导航容器
<Stack.Navigator> // 堆栈导航器
{user ? ( // 如果已登录
<Stack.Screen name="Home"> // 注册 Home 页面
{(props) => <HomeScreen {...props} onSignOut={() => setUser(null)} />} // 传入退出回调
</Stack.Screen> // 结束 Home
) : ( // 否则
<Stack.Screen name="SignIn"> // 注册 SignIn 页面
{(props) => <SignInScreen {...props} onSignIn={(name) => setUser(name)} />} // 传入登录回调
</Stack.Screen> // 结束 SignIn
)} // 结束条件
</Stack.Navigator> // 结束导航器
</NavigationContainer> // 结束容器
); // 结束 return
} // 结束 App
const styles = StyleSheet.create({ // 创建样式表
center: { flex: 1, justifyContent: 'center', alignItems: 'center', padding: 20 }, // 居中容器样式
title: { fontSize: 24, fontWeight: 'bold', marginBottom: 16 }, // 标题样式
input: { // 输入框样式
width: '100%', // 宽度占满
borderWidth: 1, // 边框宽度 1
borderColor: '#ccc', // 边框颜色
borderRadius: 6, // 圆角 6
paddingHorizontal: 10, // 水平内边距 10
height: 40, // 高度 40
marginBottom: 12, // 底部间距 12
}, // 结束 input 样式
}); // 结束样式表
核心逻辑:
user为null时,只渲染登录页面;user有值时,只渲染主应用页面;- 登录成功后
setUser,导航器自动切换到主应用; - 退出登录时
setUser(null),导航器自动切回登录页面。
不需要手动调用 navigate。 状态变了,导航器条件渲染的结果就变了。这又是"视图 = 函数(状态)"在导航层面的体现。
7.3 持久化登录状态
结合第10课会讲的 SecureStore,可以在应用启动时读取保存的令牌,恢复登录状态:
// JavaScript + React Native
useEffect(() => { // 注册副作用
SecureStore.getItemAsync('authToken') // 读取令牌
.then((token) => setUser(token ? { token } : null)) // 有令牌就设为已登录
.finally(() => setLoading(false)); // 关闭加载状态
}, []); // 空数组
loading 状态在令牌读取完成前阻止渲染,避免闪烁。
八、导航 Hook
除了通过 props 获取 navigation,React Navigation 还提供了 Hook 版本:
// JavaScript + React Native
import { useNavigation, useRoute } from '@react-navigation/native'; // 引入两个 Hook
function MyScreen() { // 定义组件
const navigation = useNavigation(); // 获取 navigation 对象
const route = useRoute(); // 获取 route 对象
return ( // 返回界面描述
<View> // 外层容器
<Text>当前页面:{route.name}</Text> // 显示当前页面名
<Button // 按钮
title="跳转" // 按钮文字
onPress={() => navigation.navigate('Details')} // 点击跳转
/> // 结束 Button
</View> // 结束容器
); // 结束 return
} // 结束 MyScreen
useNavigation() 在组件需要访问导航对象、但又不是页面组件本身时特别有用,比如嵌套在页面内部的子组件。
九、导航与"视图 = 函数(状态)"
把导航放回"视图 = 函数(状态)"的框架里:
- 导航状态(当前在哪个页面、堆栈里有哪些页面)也是一种状态;
NavigationContainer管理这个状态;- 跳转、返回、切换标签,本质上都是更新导航状态;
- 导航状态变化,导航器重新计算,显示对应的页面。
// 概念表达
导航视图 = 导航器(导航状态) // 页面由导航状态决定
条件渲染认证流程就是最好的例子:user 状态决定显示登录页面还是主应用页面。状态是输入,页面是输出。
十、练习题
练习题1:选择题——三种导航器的区别
题目:下面哪一项最适合描述"列表 → 详情"的页面流程?
A. Bottom Tab Navigator
B. Drawer Navigator
C. Stack Navigator
D. 以上都不适合
参考答案:C
解读:Stack Navigator 模拟"一摞卡片"的行为,适合顺序流程,比如列表页面跳转到详情页面,然后返回。
练习题2:填空题——导航方法
题目:补全代码,从 Home 页面跳转到 Details 页面。
// JavaScript + React Native (JSX)
<Button // 按钮
title="查看详情" // 按钮文字
onPress={() => navigation.____('Details')} // 补全导航方法
/> // 结束 Button
参考答案:navigate
解读:navigation.navigate('Details') 是跳转到指定页面的标准方法。
练习题3:改错题——参数传递
题目:下面的代码想传递参数,但读取时出错了。请找出问题。
// JavaScript + React Native
navigation.navigate('Details', { itemId: 86 }); // 跳转时传递 itemId
function DetailsScreen({ route }) { // 定义 DetailsScreen
const itemId = route.itemId; // 错误:应该从 route.params 读取
} // 结束
参考答案:应该写成 const { itemId } = route.params;。
解读:导航参数通过 route.params 访问,不是直接放在 route 上。
练习题4:代码阅读题——导航结构
题目:阅读下面的导航结构,说出在 FeedTab 中点击"查看动态详情"后,底部标签栏是否还显示。
// JavaScript + React Native (JSX)
<Tab.Navigator> // 标签导航器
<Tab.Screen name="FeedTab" component={FeedStack} /> // Feed 标签,内嵌堆栈
<Tab.Screen name="ProfileTab" component={ProfileScreen} /> // Profile 标签
</Tab.Navigator> // 结束导航器
其中 FeedStack 是:
// JavaScript + React Native (JSX)
<Stack.Navigator> // 堆栈导航器
<Stack.Screen name="FeedList" component={FeedScreen} /> // 列表页面
<Stack.Screen name="FeedDetails" component={FeedDetailsScreen} /> // 详情页面
</Stack.Navigator> // 结束导航器
参考答案:底部标签栏仍然显示。
解读:FeedStack 嵌套在 Tab 的 FeedTab 内部。在 FeedStack 内跳转时,外层 Tab 不受影响,标签栏保持可见。
练习题5:动手题——创建 Stack Navigator
题目:创建一个包含 Home 和 About 两个页面的 Stack Navigator,Home 页面有一个按钮跳转到 About。
参考答案:
// JavaScript + React Native
import { NavigationContainer } from '@react-navigation/native'; // 引入导航容器
import { createNativeStackNavigator } from '@react-navigation/native-stack'; // 引入堆栈导航器
import { View, Text, Button } from 'react-native'; // 引入需要的组件
function HomeScreen({ navigation }) { // 定义 Home 页面
return ( // 返回界面描述
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}> // 居中容器
<Text>首页</Text> // 页面文字
<Button title="去关于页" onPress={() => navigation.navigate('About')} /> // 跳转按钮
</View> // 结束容器
); // 结束 return
} // 结束 HomeScreen
function AboutScreen() { // 定义 About 页面
return ( // 返回界面描述
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}> // 居中容器
<Text>关于页</Text> // 页面文字
</View> // 结束容器
); // 结束 return
} // 结束 AboutScreen
const Stack = createNativeStackNavigator(); // 创建堆栈导航器对象
export default function App() { // 定义并默认导出 App
return ( // 返回界面描述
<NavigationContainer> // 导航容器
<Stack.Navigator> // 堆栈导航器
<Stack.Screen name="Home" component={HomeScreen} /> // 注册 Home 页面
<Stack.Screen name="About" component={AboutScreen} /> // 注册 About 页面
</Stack.Navigator> // 结束导航器
</NavigationContainer> // 结束容器
); // 结束 return
} // 结束 App
解读:NavigationContainer 是必需的外层容器。Stack.Navigator 定义页面集合,Stack.Screen 注册每个页面。
练习题6:简答题——导航方法与状态的关系
题目:为什么在认证流程中,登录成功后不需要手动调用 navigate?
参考答案:因为认证流程使用条件渲染。user 状态为 null 时渲染登录页面,有值时渲染主应用页面。登录成功更新 user 状态后,导航器自动重新计算,切换到主应用页面。
解读:这是"视图 = 函数(状态)"在导航层面的体现。导航状态变化,导航器显示对应的页面,不需要手动跳转。
练习题7:参数传递题——列表到详情
题目:有一个文章列表,点击某篇文章时跳转到详情页并传递文章的 id 和 title。
参考答案:
// JavaScript + React Native (JSX)
<Pressable // 可点击容器
onPress={() => // 点击回调
navigation.navigate('Details', { id: item.id, title: item.title }) // 跳转并传递参数
} // 结束回调
> // 开始 Pressable
<Text>{item.title}</Text> // 显示文章标题
</Pressable> // 结束 Pressable
解读:导航方法的第二个参数是传递的数据。目标页面通过 route.params 读取。
练习题8:读取参数题
题目:在详情页面中读取导航传递的 id 和 title 参数。
参考答案:
// JavaScript + React Native
function DetailsScreen({ route }) { // 定义详情页面,接收 route
const { id, title } = route.params; // 从 route.params 中解构参数
return ( // 返回界面描述
<View> // 外层容器
<Text>ID: {id}</Text> // 显示 ID
<Text>标题: {title}</Text> // 显示标题
</View> // 结束容器
); // 结束 return
} // 结束 DetailsScreen
解读:route.params 包含导航时传入的所有参数。使用解构可以方便地取出需要的值。
练习题9:配置标题题
题目:为 Stack Navigator 中的 Home 页面配置标题为"首页"。
参考答案:
// JavaScript + React Native (JSX)
<Stack.Screen // Home 页面
name="Home" // 路由名称
component={HomeScreen} // 页面组件
options={{ title: '首页' }} // 设置标题
/> // 结束 Home
解读:options.title 设置页面在导航栏中显示的标题。也可以在整个 Stack.Navigator 上设置 screenOptions 来统一配置。
练习题10:Tab Navigator 题
题目:创建一个包含三个标签的底部标签导航:首页、搜索、我的。
参考答案:
// JavaScript + React Native
const Tab = createBottomTabNavigator(); // 创建标签导航器对象
export default function App() { // 定义并默认导出 App
return ( // 返回界面描述
<NavigationContainer> // 导航容器
<Tab.Navigator> // 标签导航器
<Tab.Screen name="Home" component={HomeScreen} /> // 注册 Home 标签
<Tab.Screen name="Search" component={SearchScreen} /> // 注册 Search 标签
<Tab.Screen name="Profile" component={ProfileScreen} /> // 注册 Profile 标签
</Tab.Navigator> // 结束导航器
</NavigationContainer> // 结束容器
); // 结束 return
} // 结束 App
解读:Tab Navigator 适合平级模块的切换。每个标签对应一个独立的页面组件。
练习题11:嵌套导航题
题目:在 Tab Navigator 的 Home 标签中嵌套一个 Stack Navigator,包含 HomeList 和 HomeDetails 两个页面。
参考答案:
// JavaScript + React Native
const Stack = createNativeStackNavigator(); // 创建堆栈导航器对象
function HomeStack() { // 定义 Home 的堆栈导航器
return ( // 返回界面描述
<Stack.Navigator> // 堆栈导航器
<Stack.Screen name="HomeList" component={HomeListScreen} /> // 列表页面
<Stack.Screen name="HomeDetails" component={HomeDetailsScreen} /> // 详情页面
</Stack.Navigator> // 结束导航器
); // 结束 return
} // 结束 HomeStack
const Tab = createBottomTabNavigator(); // 创建标签导航器对象
export default function App() { // 定义并默认导出 App
return ( // 返回界面描述
<NavigationContainer> // 导航容器
<Tab.Navigator> // 标签导航器
<Tab.Screen name="Home" component={HomeStack} /> // Home 标签,内嵌堆栈
<Tab.Screen name="Profile" component={ProfileScreen} /> // Profile 标签
</Tab.Navigator> // 结束导航器
</NavigationContainer> // 结束容器
); // 结束 return
} // 结束 App
解读:嵌套导航器是真实 App 的常见结构。外层 Tab 负责模块切换,内层 Stack 负责模块内的页面流程。
练习题12:导航 Hook 题
题目:在页面内部的子组件中获取 navigation 对象。
参考答案:
// JavaScript + React Native
import { useNavigation } from '@react-navigation/native'; // 引入 useNavigation Hook
function MyButton() { // 定义子组件
const navigation = useNavigation(); // 获取 navigation 对象
return ( // 返回界面描述
<Button // 按钮
title="跳转" // 按钮文字
onPress={() => navigation.navigate('Details')} // 点击跳转
/> // 结束 Button
); // 结束 return
} // 结束 MyButton
解读:useNavigation 让非页面组件也能访问导航对象。在页面内部的深层子组件中特别有用。
练习题13:认证流程题
题目:用 user 状态控制显示登录页面还是主应用页面。
参考答案:
// JavaScript + React Native (JSX)
<Stack.Navigator> // 堆栈导航器
{user ? ( // 如果已登录
<Stack.Screen name="Home" component={HomeScreen} /> // 渲染主应用
) : ( // 否则
<Stack.Screen name="SignIn" component={SignInScreen} /> // 渲染登录页面
)} // 结束条件
</Stack.Navigator> // 结束导航器
解读:条件渲染认证流程不需要手动 navigate。user 状态变化后,导航器自动切换。
练习题14:综合导航结构题
题目:设计一个包含以下结构的导航:
- 底部标签:首页、我的;
- 首页内部:文章列表 → 文章详情;
- 我的内部:个人资料 → 设置。
参考答案:
// JavaScript + React Native
const Tab = createBottomTabNavigator(); // 创建标签导航器对象
const Stack = createNativeStackNavigator(); // 创建堆栈导航器对象
function HomeStack() { // 定义首页的堆栈导航器
return ( // 返回界面描述
<Stack.Navigator> // 堆栈导航器
<Stack.Screen name="ArticleList" component={ArticleListScreen} /> // 列表页面
<Stack.Screen name="ArticleDetail" component={ArticleDetailScreen} /> // 详情页面
</Stack.Navigator> // 结束导航器
); // 结束 return
} // 结束 HomeStack
function ProfileStack() { // 定义我的页面的堆栈导航器
return ( // 返回界面描述
<Stack.Navigator> // 堆栈导航器
<Stack.Screen name="ProfileMain" component={ProfileScreen} /> // 个人资料页面
<Stack.Screen name="Settings" component={SettingsScreen} /> // 设置页面
</Stack.Navigator> // 结束导航器
); // 结束 return
} // 结束 ProfileStack
export default function App() { // 定义并默认导出 App
return ( // 返回界面描述
<NavigationContainer> // 导航容器
<Tab.Navigator> // 标签导航器
<Tab.Screen name="Home" component={HomeStack} /> // 首页标签
<Tab.Screen name="Profile" component={ProfileStack} /> // 我的标签
</Tab.Navigator> // 结束导航器
</NavigationContainer> // 结束容器
); // 结束 return
} // 结束 App
解读:这是典型的"Tab 嵌套 Stack"结构。每个标签内部有独立的页面流程,标签之间平级切换。
练习题15:综合应用题——带导航的迷你博客
题目:创建一个迷你博客应用:
- 底部有两个标签:文章和关于;
- 文章标签内是一个文章列表,点击文章跳转到详情页;
- 详情页显示文章标题和正文;
- 关于标签显示应用介绍。
参考答案:
// JavaScript + React Native
import { NavigationContainer } from '@react-navigation/native'; // 引入导航容器
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; // 引入底部标签导航器
import { createNativeStackNavigator } from '@react-navigation/native-stack'; // 引入堆栈导航器
import { View, Text, FlatList, Pressable, StyleSheet } from 'react-native'; // 引入需要的组件
const posts = [ // 文章数据
{ id: '1', title: 'React Native 入门', body: 'React Native 让你用 JavaScript 构建原生应用。' }, // 第一篇文章
{ id: '2', title: '组件与 Props', body: '组件是 React Native 的基本单元,props 用于传递数据。' }, // 第二篇文章
{ id: '3', title: '导航基础', body: 'React Navigation 是主流的导航方案。' }, // 第三篇文章
]; // 结束数据
function ArticleListScreen({ navigation }) { // 定义文章列表页面
return ( // 返回界面描述
<FlatList // 高效列表
data={posts} // 数据源
keyExtractor={(item) => item.id} // 从每项提取唯一 key
renderItem={({ item }) => ( // 如何渲染每一项
<Pressable // 可点击容器
style={styles.item} // 应用 item 样式
onPress={() => navigation.navigate('ArticleDetail', { id: item.id })} // 跳转详情页
> // 开始 Pressable
<Text style={styles.itemTitle}>{item.title}</Text> // 显示标题
</Pressable> // 结束 Pressable
)} // 结束 renderItem
ItemSeparatorComponent={() => <View style={styles.separator} />} // 分隔线
/> // 结束 FlatList
); // 结束 return
} // 结束 ArticleListScreen
function ArticleDetailScreen({ route }) { // 定义文章详情页面
const { id } = route.params; // 从路由参数中取出 id
const post = posts.find((p) => p.id === id); // 根据 id 找到文章
return ( // 返回界面描述
<View style={styles.center}> // 居中容器
<Text style={styles.title}>{post.title}</Text> // 显示标题
<Text style={styles.body}>{post.body}</Text> // 显示正文
</View> // 结束容器
); // 结束 return
} // 结束 ArticleDetailScreen
function AboutScreen() { // 定义关于页面
return ( // 返回界面描述
<View style={styles.center}> // 居中容器
<Text style={styles.title}>关于</Text> // 页面标题
<Text>一个用 React Navigation 构建的迷你博客。</Text> // 应用介绍
</View> // 结束容器
); // 结束 return
} // 结束 AboutScreen
const HomeStack = createNativeStackNavigator(); // 创建首页堆栈导航器对象
function HomeStackScreen() { // 定义首页堆栈
return ( // 返回界面描述
<HomeStack.Navigator> // 堆栈导航器
<HomeStack.Screen // 列表页面
name="ArticleList" // 路由名称
component={ArticleListScreen} // 页面组件
options={{ title: '文章' }} // 页面标题
/> // 结束列表页面
<HomeStack.Screen // 详情页面
name="ArticleDetail" // 路由名称
component={ArticleDetailScreen} // 页面组件
options={{ title: '详情' }} // 页面标题
/> // 结束详情页面
</HomeStack.Navigator> // 结束导航器
); // 结束 return
} // 结束 HomeStackScreen
const Tab = createBottomTabNavigator(); // 创建标签导航器对象
export default function App() { // 定义并默认导出 App
return ( // 返回界面描述
<NavigationContainer> // 导航容器
<Tab.Navigator> // 标签导航器
<Tab.Screen // 文章标签
name="Home" // 路由名称
component={HomeStackScreen} // 组件是首页堆栈
options={{ title: '文章' }} // 标签标题
/> // 结束文章标签
<Tab.Screen // 关于标签
name="About" // 路由名称
component={AboutScreen} // 关于页面
options={{ title: '关于' }} // 标签标题
/> // 结束关于标签
</Tab.Navigator> // 结束导航器
</NavigationContainer> // 结束容器
); // 结束 return
} // 结束 App
const styles = StyleSheet.create({ // 创建样式表
center: { flex: 1, justifyContent: 'center', alignItems: 'center', padding: 20 }, // 居中容器样式
title: { fontSize: 24, fontWeight: 'bold', marginBottom: 12 }, // 标题样式
body: { fontSize: 16, lineHeight: 24, color: '#444' }, // 正文样式
item: { padding: 16 }, // 列表项样式
itemTitle: { fontSize: 18 }, // 列表项标题样式
separator: { height: 1, backgroundColor: '#eee' }, // 分隔线样式
}); // 结束样式表
解读:这个应用综合运用了 Stack Navigator、Bottom Tab Navigator、嵌套导航、参数传递和列表渲染。文章标签内部用 Stack 实现列表到详情的流程,关于标签是一个独立页面。底部标签栏在两个模块之间切换。
十一、总结
这一课我们学习了 React Navigation 的核心用法:
- React Navigation 是 React Native 的主流导航方案;
- Stack Navigator 适合顺序流程,模拟"一摞卡片";
- 页面间通过
navigation.navigate('Name', params)传递参数,通过route.params读取; - Bottom Tab Navigator 适合平级模块切换;
- Drawer Navigator 适合全局菜单;
- 嵌套导航器是真实 App 的常见结构,Tab 嵌套 Stack 尤其常见;
- 认证流程利用条件渲染,不需要手动跳转;
useNavigation让非页面组件也能访问导航对象。
记住一句话:
导航也是一种状态,页面切换就是导航状态的变化。
第6课预告
下一课,我们进入 【React Native娓娓道来】第6课 状态管理的进阶之路:Context、useReducer 与跨组件共享状态。
更多推荐



所有评论(0)