为什么需要导航库
在原生 iOS 开发中,UIKit 提供了 UINavigationController 管理页面栈,UITabBarController 管理标签栏。Android 有 Fragment、Activity 和 Jetpack Navigation。这些都是操作系统层面内置的导航机制。
React Native 在 JS 层工作,需要用 JS 模拟这些导航行为,并正确地与底层平台的手势系统(iOS 右滑返回、Android 物理返回键)对接。React Navigation 是目前社区最成熟的解决方案,它用 JS 完整实现了各种导航模式,并针对 iOS 和 Android 提供了平台特定的动画效果。
导航核心名词
NavigationContainer
导航树的根容器,管理导航状态(类似 Redux Store)。整个应用只有一个 NavigationContainer,通常放在 App.tsx 的顶层。
Stack Navigator
栈式导航,页面以堆栈方式管理。push 将新页面压栈,pop/goBack 弹出返回上一页。iOS 有右滑返回手势,Android 有物理返回键。
Tab Navigator
标签栏导航,屏幕底部(iOS)或顶部(Android MD 风格)的多标签切换。每个 Tab 有独立的导航栈,切换 Tab 时状态保留。
Drawer Navigator
侧边抽屉导航,从屏幕边缘滑出菜单。常用于设置、个人中心等次要导航入口。
Screen
导航中的单个页面单元,对应一个组件。可配置标题、标题栏按钮、动画类型等选项。
useNavigation
Hook,在任意子组件中获取导航对象(navigation),无需通过 props 层层传递。提供 navigate、push、goBack、setOptions 等方法。
route.params
页面接收的路由参数对象。通过 navigate('Detail', { id: 123 }) 传递,在目标页面用 route.params.id 接收。
DeepLink(深度链接)
通过 URL 直接打开应用内的特定页面。格式如 myapp://post/123 或 https://myapp.com/post/123(Universal Links)。
导航架构总览
典型三层嵌套导航结构
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
NavigationContainer ← 全局状态容器
└── BottomTabNavigator ← 底部 Tab 导航
├── Tab: Home
│ └── StackNavigator ← Tab 内的栈导航
│ ├── Screen: HomeScreen
│ ├── Screen: PostDetail ← navigate('PostDetail', {id})
│ └── Screen: UserProfile
├── Tab: Explore
│ └── StackNavigator
│ └── Screen: ExploreScreen
└── Tab: Profile
└── StackNavigator
├── Screen: ProfileScreen
└── Screen: EditProfile
手势说明:
• iOS:右滑 = Stack goBack,下滑 = Modal dismiss
• Android:物理返回键 = Stack goBack,由框架自动处理
• Tab 切换时各 Stack 状态独立保留(不会重置)
安装与基础配置
# 安装核心依赖
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,强烈推荐 Expo Router(基于文件系统的路由)。它的工作方式类似 Next.js——文件路径即路由路径,自动处理深度链接,TypeScript 类型自动生成,无需手动维护 ParamList。新项目首选 Expo Router v3。
iOS vs Android 动画差异
Stack Navigator 在 iOS 默认使用右滑进入/左退动画(UINavigationController 风格),Android 默认使用上下淡入淡出(Material Design 风格)。可以通过
animation 选项统一,但建议保留平台默认行为,用户更熟悉。