React Navigation

1,165 阅读3分钟

这是我参与8月更文挑战的第9天,活动详情查看:8月更文挑战

基本使用

搭建项目

$ npx react-native init musicqq --template react-native-template-typescript

安装

$ npm install @react-navigation/native
or
$ yarn add @react-navigation/native

上面命令执行成功后,就安装了navigation的核心组件库了,但是在使用导航过程会涉及一些手势响应、动画交互、原生兼容等等;所以接下来安装一些支持这些操作的第三方依赖。

$ npm i react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view
or
$ yarn add react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view

React Native版本在0.6以上就不用手动link,React Native会自动link。但是iOS端还是需要手动安装;进入ios文件夹,然后运行命令:pod install(前提是已经安装了cocoapods环境)

在web浏览器中,您可以使用锚(<a>)标记链接到不同的页面。当用户单击链接时,URL会被推送到浏览器历史堆栈中。当用户按下后退按钮时,浏览器将从历史堆栈顶部弹出该项,因此活动页面现在就是以前访问过的页面。React Native不像web浏览器那样有一个内置的api;React Navigation的堆栈导航器就提供了一种方式来在也,看之间转换和管理导航历史。如果你的应用程序只使用一个堆栈导航器,那么它在概念上类似于web浏览器处理导航状态的方式——当用户与应用程序交互时,你的应用程序从导航堆栈中推入和弹出项目,这导致用户看到不同的视图内容。这在网页浏览器和React Navigation中工作的一个关键区别是,React Navigation的堆栈导航器提供了手势和动画,你可以在Android和iOS上导航堆栈中的路由;安装堆栈式导航组件 @react-navigation/stack

$ yarn add @react-navigation/stack

实现Tab界面切换、界面间导航

API定义:StackNavigator(RouteConfigs, StackNavigatorConfig)、TabNavigator(RouteConfigs, TabNavigatorConfig)

  • 安装依赖:

    $ yarn add @react-navigation/bottom-tabs
    
  • 创建Tab

    // tab按钮的参数列表
    export type BottomTabParamList = {
        Home: undefined;
        List: undefined;
        Found: undefined;
        Me: undefined;
    };
    
    // Tab中包含 Navigator 和 Screen
    const Tab = createBottomTabNavigator<BottomTabParamList>();
    
  • 引入组件,编写路由配置

// ....
import Home from '../pages/home/Home';
import List from '../pages/list/List';
import Found from '../pages/found/Found';
import Me from '../pages/me/Me';

// ...

<Tab.Navigator
    tabBarOptions={{
      activeTintColor: '#f86442', // 修改tabbar激活颜色
    }}>
    <Tab.Screen
        name="Home"
        component={Home}
        options={{ tabBarLabel: '首页' }}
      />
    <Tab.Screen
        name="List"
        component={List}
        options={{ tabBarLabel: '榜单' }}
      />
    <Tab.Screen
        name="Found"
        component={Found}
        options={{ tabBarLabel: '发现' }}
      />
    <Tab.Screen
        name="Me"
        component={Me}
        options={{ tabBarLabel: '我的' }}
      />
</Tab.Navigator>

// ...

正常情况下,页面底部就会出现四个tab按钮了,可以切换到对应的页面;但是切换后发现顶部的title都是“首页”,没有改变,接下来就实现顶部的title切换;

// route的类型
type Route = RouteProp<RootStackParamList, 'BottomTabs'> & {
    state?: TabNavigationState<BottomTabParamList>;
};

// 定义组件的属性类型
interface IProps {
    navigation: RootStackNavigation;
    route: Route;
}

// 获取每个页面的标题
getHeaderTitle(route: Route): string {
  	// 传入当前页面的路由,获取路由的名称;如果没有就默认 Home
    const routeName = getFocusedRouteNameFromRoute(route) ?? 'Home';

    switch (routeName) {
        case 'Home':
        		return '首页';
        case 'List':
        		return '榜单';
        case 'Found':
        		return '发现';
        case 'Me':
        		return '我的';
        default:
        		return '首页';
    }
}

componentDidUpdate() {
    const { navigation, route } = this.props;
    navigation.setOptions({
      	headerTitle: this.getHeaderTitle(route),
    });
}

这样就完整的实现了头部title切换,完整代码如下:

import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import React, { Component } from 'react';
import {
   getFocusedRouteNameFromRoute,
   RouteProp,
   TabNavigationState,
} from '@react-navigation/native';
import { RootStackNavigation, RootStackParamList } from './index';
import Home from '../pages/home/Home';
import List from '../pages/list/List';
import Found from '../pages/found/Found';
import Me from '../pages/me/Me';

export type BottomTabParamList = {
     Home: undefined;
     List: undefined;
     Found: undefined;
     Me: undefined;
};

const Tab = createBottomTabNavigator<BottomTabParamList>();

type Route = RouteProp<RootStackParamList, 'BottomTabs'> & {
 		state?: TabNavigationState<BottomTabParamList>;
};

interface IProps {
   navigation: RootStackNavigation;
   route: Route;
}

export default class BottomTabs extends Component<IProps> {
 // 获取每个页面的标题
 getHeaderTitle(route: Route): string {
     const routeName = getFocusedRouteNameFromRoute(route) ?? 'Home';

     switch (routeName) {
         case 'Home':
             return '首页';
         case 'List':
             return '榜单';
         case 'Found':
             return '发现';
         case 'Me':
             return '我的';
         default:
             return '首页';
     }
 }

 componentDidUpdate() {
     const { navigation, route } = this.props;
     navigation.setOptions({
         headerTitle: this.getHeaderTitle(route),
     });
 }
 render() {
     return (
         <Tab.Navigator
             tabBarOptions={{
                 activeTintColor: '#f86442', // 修改tabbar激活颜色
             }}>
             <Tab.Screen
                 name="Home"
                 component={Home}
                 options={{ tabBarLabel: '首页' }}
             />
             <Tab.Screen
                 name="List"
                 component={List}
                 options={{ tabBarLabel: '榜单' }}
             />
             <Tab.Screen
                 name="Found"
                 component={Found}
                 options={{ tabBarLabel: '发现' }}
             />
             <Tab.Screen
                 name="Me"
                 component={Me}
                 options={{ tabBarLabel: '我的' }}
             />
        </Tab.Navigator>);
 		}
}

StackNavigator还提供了onNavigationStateChange回调方法,用来监听导航状态的改变。实现了界面跳转和切换,那么就该来增加下界面之间的感情了,来看看如何实现界面之间的传值和取值。

界面间跳转、传值、取值

在界面组件注入到StackNavigator中时,界面组件就被赋予了navigation属性,即在界面组件中可以通过this.props.navigation 获取并进行一些操作。

navigation属性中提供了很多的函数简化界面间操作,简单列举几点:

  • 通过navigate函数实现界面之间跳转:

    const { navigation } = this.props;
    navigation.navigate('Detail'); // 跳转到详情页
    

    参数为我们在StackNavigator注册界面组件时的名称。同样也可以从当前页面返回到上一页:

    const { navigation } = this.props;
    navigation.goBack(); // 返回上一页
    
  • 跳转时传值:

    const { navigation } = this.props;
    navigation.navigate('Detail', { topId: 'see3x5dfw6' }); // 路由传参
    

    第一个参数同样为要跳转的界面组件名称,第二个参数为要传递的参数,topid可以理解为key,后面即传递的参数。

  • 获取路由参数:

    const { topId } = this.props.route.params; // 获取topId
    

    通过route.params来获取传来的参数,后面为key值。此处为topId。