在App中,底部 TabBar 导航和顶部的导航栏导航是最常见的页面导航方式,而 React Native 官方推荐的第三方库是 @react-navigation ,正好今年 React Navigation 发布了5.0版本,与前面的版本差别还挺大,不过使用上更方便了。本文主要讲解 React Navigation 5.0及以上版本的使用。

安装

安装核心包

# NPM
npm install @react-navigation/native
# Yarn
yarn add @react-navigation/native

安装依赖

# NPM
npm install react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view
#Yarn
yarn add react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view

React Native0.60及以上版本是自动链接库的,不需要手动运行 react-native link ,但如果你用 React Native 开发的是iOS,还需要手动安装 pods 来完成库的链接:

npx pod-install ios

把下面这行代码放在入口文件的顶部,比如 index.js 或者 App.js

import 'react-native-gesture-handler';

最后需要用 NavigationContainer 将整个App包裹起来,这个代码一般也是放在入口文件,类似下面这样:

import 'react-native-gesture-handler';
import * as React from 'react';
import { NavigationContainer } from '@react-navigation/native';

export default function App() {
  return (
    <NavigationContainer>{/* Rest of your app code */}</NavigationContainer>
  );
}

注意:如果你同时也使用了 Redux 框架,需要把 Provider 放在最外层,将 NavigationContainer 包裹在次外层,类似下面这样:

export default class App() {
  return (
    <Provider store={store}>
      <NavigationContainer>
        {/* Screen configuration */}
      </NavigationContainer>
    </Provider>
  );
}

使用

React Navigation有多种导航方式: Stack NavigationTab NavigationDrawer Navigation 。这里主要讲 Stack NavigationTab Navigation

顶部导航栏导航

首先需要安装相应的库:

#NPM
npm install @react-navigation/stack
#Yarn
yarn add @react-navigation/stack

Stack Navigation 使用上相对来说比较简单,只需要把需要导航的页面组件封装成 Stack.Screen ,然后包裹在 Stack.Navigator 就可以了,最上面的 Stack.Screen 就是默认页面。

import * as React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';

const Stack = createStackNavigator();

function App() {
  return (
    <NavigationContainer>
      <Stack.Navigator>
        <Stack.Screen key="Home" name="Home" component={HomeScreen} />
      </Stack.Navigator>
      <Stack.Screen key="detail" name="Detail" component={DetailScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

export default App;

也可以在 Stack.Screen 中设置导航栏的属性,设置导航栏属性 options 有两种方式,一种是带 route 参数的,一种是直接设置

/* 带route参数的options */
<Stack.Screen
    key={name}
    name={name}
    component={component}
    options={({route}) => ({
        headerTitle: route.name,
        headerStyle: {
            backgroundColor: '#01aaff',
        },
        headerTintColor: '#fff',
        headerTitleStyle: {
            fontWeight: 'bold',
        },
        headerRight: () => (
            <Button
              onPress={() => alert('This is a button!')}
              title="Info"
              color="#fff"
            />
          ),
        headerShown: true
    })}
/>
/* 直接设置options */
<Stack.Screen
    key={name}
    name={name}
    component={component}
    options={{
        headerTitle: 'name',
        headerStyle: {
            backgroundColor: '#01aaff',
        },
        headerTintColor: '#fff',
        headerTitleStyle: {
            fontWeight: 'bold',
        },
        headerRight: () => (
            <Button
              onPress={() => alert('This is a button!')}
              title="Info"
              color="#fff"
            />
          ),
        headerShown: true
    }}
/>

注意: React 框架使用的是虚拟 DOM ,使用 diff 算法刷新页面显示,在 Stack.Screen 中,需要添加 key 属性,否则会报警告。

在需要跳转到其他页面时只需要以下代码就可以了,后面跟的是参数。

function HomeScreen({ navigation }) {
  return (
    <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
      <Text>Home Screen</Text>
      <Button
        title="Go to Details"
        onPress={() => {
          /* 1. Navigate to the Details route with params */
          navigation.navigate('Details', {
            itemId: 86,
            otherParam: 'anything you want here',
          });
        }}
      />
    </View>
  );
}

如果是用 class 方式创建的页面,代码会像下面这样:

export default class HomeScreen extends Component {
    render() {
        return (
            <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
                <Text>Home Screen</Text>
                <Button
                    title="Go to Details"
                    onPress={() => {
                        this.props.navigation.navigate('Details', {
                            itemId: 86,
                            otherParam: 'anything you want here',
                        });
                    }}
                />
            </View>
        );
    }
}

注意:导航跳转可以使用 navigation.navigate 或者 navigation.push 。如果使用 navigate ,会查找当前堆栈中是否有名字一样的路由,如果没有才创建新的路由并跳转,而 push 则会直接创建一个新的路由并跳转,也就是说可以重复多次跳转同一个页面。

堆栈导航的返回方式如下:

// 返回到上一页面
navigation.goBack()
// 返回到堆栈里第一个页面
navigation.popToPop()

底部导航栏导航

除了顶部导航栏,最常用的就是底部导航栏了,首先也需要安装相应的库:

# NPM
npm install @react-navigation/bottom-tabs
# Yarn
yarn add @react-navigation/bottom-tabs

底部导航栏的使用方法类似,需要将这些页面组件包装成 Tab.Screen ,导航则由框架内部完成,不需要手动控制:

import * as React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';

const Tab = createBottomTabNavigator();

export default function App() {
  return (
    <NavigationContainer>
      <Tab.Navigator>
        <Tab.Screen name="Home" component={HomeScreen} />
        <Tab.Screen name="Settings" component={SettingsScreen} />
      </Tab.Navigator>
    </NavigationContainer>
  );
}

底部导航栏属性可以按如下方式设置:

<Tab.Navigator
        screenOptions={({ route }) => ({
          tabBarIcon: ({ focused, color, size }) => {
            let iconName;

            if (route.name === 'Home') {
              iconName = focused
                ? 'ios-information-circle'
                : 'ios-information-circle-outline';
            } else if (route.name === 'Settings') {
              iconName = focused ? 'ios-list-box' : 'ios-list';
            }
            return <Ionicons name={iconName} size={size} color={color} />;
          },
        })}
        tabBarOptions={{
          activeTintColor: 'tomato',
          inactiveTintColor: 'gray',
        }}
      >
        <Tab.Screen name="Home" component={HomeScreen} />
        <Tab.Screen name="Settings" component={SettingsScreen} />
      </Tab.Navigator>

顶部堆栈导航栏与底部导航栏嵌套

Stack NavigationTab Navigation 可以相互多层嵌套,比如登录页跳转到带底部导航栏的主页面,需要把登录相关面和 Tab.navigator 一同放到同一个堆栈中,但官方不推荐这种方式,嵌套层次太多会导致维护起来特别麻烦。官方推荐使用一个变量控制当前显示的页面,把不相关的模块隔离在不同的堆栈中,类似下面这样:

const commonScreens = {
  Help: HelpScreen,
};

const authScreens = {
  SignIn: SignInScreen,
  SignUp: SignUpScreen,
};

const userScreens = {
  Home: HomeScreen,
  Profile: ProfileScreen,
};

<Stack.Navigator>
  {Object.entries({
    ...commonScreens,
    ...(isLoggedIn ? userScreens : authScreens),
  }).map(([name, component]) => (
    <Stack.Screen name={name} component={component} />
  ))}
</Stack.Navigator>;

isLoggedIn 这样的全局变量动态刷新页面使用 Redux 架构实现起来会简单一些,具体实现可以参考 这篇教程

相关文章