Chapter 03

React Navigation 路由

构建多页面移动应用的导航体系:Stack 栈导航、Tab 标签栏、Drawer 抽屉,以及类型安全的路由传参。

为什么需要导航库

在原生 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 选项统一,但建议保留平台默认行为,用户更熟悉。