为什么需要导航库
在原生 iOS 开发中,UIKit 提供了 UINavigationController 管理页面栈,UITabBarController 管理标签栏。Android 有 Fragment、Activity 和 Jetpack Navigation。这些都是操作系统层面内置的导航机制。
React Native 在 JS 层工作,需要用 JS 模拟这些导航行为,并正确地与底层平台的手势系统(iOS 右滑返回、Android 物理返回键)对接。React Navigation 是目前社区最成熟的解决方案,它用 JS 完整实现了各种导航模式,并针对 iOS 和 Android 提供了平台特定的动画效果。
导航核心名词
导航架构总览
安装与基础配置
# 安装核心依赖
npx expo install @react-navigation/native
npx expo install react-native-screens react-native-safe-area-context
# Stack 导航器
npx expo install @react-navigation/native-stack
# Tab 导航器
npx expo install @react-navigation/bottom-tabs
# Drawer 导航器
npx expo install @react-navigation/drawer react-native-gesture-handler react-native-reanimated
完整三层嵌套导航(TypeScript)
// navigation/types.ts — 路由参数类型定义
import { NativeStackScreenProps } from '@react-navigation/native-stack';
import { BottomTabScreenProps } from '@react-navigation/bottom-tabs';
// 主 Stack 的参数表
export type RootStackParamList = {
Home: undefined; // 无参数
PostDetail: { id: string; title: string }; // 必须传 id 和 title
UserProfile: { userId: string };
Modal: { message: string };
};
// Tab 导航的参数表
export type TabParamList = {
Feed: undefined;
Explore: undefined;
Profile: undefined;
};
// 组件 Props 类型推导
export type PostDetailProps = NativeStackScreenProps<
RootStackParamList,
'PostDetail'
>;
// App.tsx — 导航根配置
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
const Stack = createNativeStackNavigator<RootStackParamList>();
const Tab = createBottomTabNavigator<TabParamList>();
// Tab 导航(嵌套在 Stack 中作为一个 Screen)
function TabNavigator() {
return (
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color }) => {
const icons: Record<string, string> = {
Feed: focused ? '🏠' : '🏡',
Explore: focused ? '🔍' : '🔎',
Profile: focused ? '👤' : '👥',
};
return <Text>{icons[route.name]}</Text>;
},
tabBarActiveTintColor: '#38bdf8',
tabBarStyle: { backgroundColor: '#071220' },
headerShown: false,
})}
>
<Tab.Screen name="Feed" component={FeedScreen} />
<Tab.Screen name="Explore" component={ExploreScreen} />
<Tab.Screen name="Profile" component={ProfileScreen} />
</Tab.Navigator>
);
}
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
// Tab 导航作为默认页面
<Stack.Screen
name="Home"
component={TabNavigator}
options={{ headerShown: false }}
/>
<Stack.Screen
name="PostDetail"
component={PostDetailScreen}
options={({ route }) => ({ title: route.params.title })}
/>
<Stack.Screen
name="Modal"
component={ModalScreen}
options={{ presentation: 'modal' }} // iOS sheet 样式
/>
</Stack.Navigator>
</NavigationContainer>
);
}
页面间传参与接收
// 发送方:FeedScreen.tsx
import { useNavigation } from '@react-navigation/native';
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
type NavProp = NativeStackNavigationProp<RootStackParamList>;
function FeedScreen() {
const navigation = useNavigation<NavProp>();
return (
<Pressable onPress={() =>
navigation.navigate('PostDetail', {
id: 'post-123',
title: '这是一篇好文章',
})
}>
<Text>查看详情</Text>
</Pressable>
);
}
// 接收方:PostDetailScreen.tsx
function PostDetailScreen({ route }: PostDetailProps) {
const { id, title } = route.params; // ← TypeScript 自动推断类型
return (
<View>
<Text>帖子 ID: {id}</Text>
<Text>标题: {title}</Text>
</View>
);
}
深度链接配置
// App.tsx — linking 配置
const linking = {
prefixes: ['myapp://', 'https://myapp.com'],
config: {
screens: {
Home: {
screens: {
Feed: 'feed', // myapp://feed
Profile: 'profile', // myapp://profile
},
},
PostDetail: 'post/:id', // myapp://post/123
UserProfile: 'user/:userId', // myapp://user/abc
},
},
};
export default function App() {
return (
<NavigationContainer linking={linking}>
{/* ...导航栈 */}
</NavigationContainer>
);
}
Expo Router:文件系统路由
Expo Router 让 React Native 的路由像 Next.js 一样直观——文件路径即 URL 路径,约定大于配置:
# Expo Router 项目文件结构(app/ 目录即路由)
app/
├── _layout.tsx # 根布局(设置 NavigationContainer)
├── index.tsx # 首页 /
├── (tabs)/ # Tab 导航组(括号 = 路由组,不影响 URL)
│ ├── _layout.tsx # Tab 布局配置(图标、标签名)
│ ├── index.tsx # Tab 首页
│ └── profile.tsx # /profile
├── (auth)/ # 认证流(独立的导航栈)
│ ├── login.tsx # /login
│ └── register.tsx # /register
├── post/
│ └── [id].tsx # /post/123(动态路由,[id]为参数)
└── +not-found.tsx # 404 页面
// app/(tabs)/_layout.tsx — Tab 导航配置
import { Tabs } from 'expo-router';
export default function TabLayout() {
return (
<Tabs screenOptions={{ tabBarActiveTintColor: '#38bdf8' }}>
<Tabs.Screen
name="index"
options={{
title: '首页',
tabBarIcon: ({ color }) => <HomeIcon color={color} />,
}}
/>
</Tabs>
);
}
// app/post/[id].tsx — 访问动态路由参数
import { useLocalSearchParams } from 'expo-router';
export default function PostDetail() {
const { id } = useLocalSearchParams<{ id: string }>();
return <Text>帖子 ID:{id}</Text>;
}
// 导航跳转(命令式)
import { router } from 'expo-router';
router.push('/post/123'); // 跳转(有返回)
router.replace('/(tabs)/'); // 替换(登录后跳首页,不保留登录页历史)
router.back(); // 返回上一页
导航与路由的核心要点:① React Navigation 是 RN 路由的事实标准,分三类导航器:Stack(页面堆栈,有返回键)、Tab(底部标签切换)、Drawer(侧边栏抽屉),通常嵌套使用;② 屏幕参数通过 route.params 接收,TypeScript 用户必须定义 ParamList 类型确保类型安全;③ Expo Router(基于文件系统)是新项目首选:文件路径即路由路径,TypeScript 类型自动生成;④ 深度链接(Deep Link)让 URL(myapp://post/123)直接打开 App 内页,需要在 linking.config 中映射;⑤ 认证流应独立为导航栈,登录成功用 router.replace 而非 push,防止用户通过返回键回到登录页;⑥ iOS 默认右滑动画(UINavigationController 风格),Android 默认 Material 淡入动画,建议保留平台默认行为。