开始使用React Native导航库

338 阅读11分钟

Getting Started with the React Native Navigation Library

React Native应用开发中最重要的方面之一是导航。它能让用户到达他们要找的页面。这就是为什么选择最好的导航库来满足你的需求很重要。

如果你的应用程序有很多屏幕和相对复杂的UI,可能值得探索React Native Navigation而不是React Navigation。这是因为React导航总是会有性能瓶颈,因为它与应用程序的其他部分是在同一个JavaScript线程上工作的。你的用户界面越复杂,需要传递给该桥的数据就越多,这就有可能使它变慢。

在本教程中,我们将研究Wix的React Native Navigation库,这是一个可供选择的导航库,适合那些为其React Native应用寻找更流畅的导航性能的人。

前提条件

学习本教程需要有React和React Native的知识。之前使用React Navigation等导航库的经验是可选的。

读者还需要在本地安装NodeYarn,以及一个React Native开发环境。你可以在这里找到设置的帮助。请务必选择React Native CLI Quickstart

应用程序概述

为了演示如何使用这个库,我们将创建一个使用它的简单应用。该应用总共有五个屏幕。

  • 初始化:这是该应用的初始屏幕。如果用户已经登录,它将自动导航到主屏幕。如果没有,用户会被导航到登录屏幕。
  • 登录:这允许用户登录,以便他们可以查看主页、画廊和饲料。为了简化事情,登录将只是模拟的;不涉及实际的认证代码。在这个屏幕上,用户也可以进入忘记密码的屏幕。
  • 忘记密码:一个填充屏幕,要求提供用户的电子邮件地址。这将只是用来演示堆栈导航。
  • 主页:用户登录时将看到的初始屏幕。从这里,他们也可以通过底部的标签导航到画廊或饲料屏幕。
  • 图库:一个显示照片库用户界面的填充屏幕。
  • 馈送:一个显示新闻馈送界面的填充屏幕。

以下是该应用程序的外观。

React Native Navigation demo gif

你可以在这个GitHub repo上找到样本应用程序的源代码。

引导应用程序

让我们从生成一个新的React Native项目开始。

npx react-native init RNNavigation

接下来,安装应用程序的依赖项。

  • react-native-navigation:我们要使用的导航库。由于它的名字很长,从现在开始我将把它称为RNN。
  • @react-native-async-storage/async-storage: 用于将数据保存到应用程序的本地存储。
  • react-native-vector-icons:用于显示底部标签导航的图标。
yarn add react-native-navigation @react-native-async-storage/async-storage react-native-vector-icons

一旦这些都安装好了,我们需要将相应的本地模块链接到应用程序。注意,我将只介绍React Native 0.60及以上版本的模块链接。如果你使用的是旧版本的React Native,你必须通过老方法来做,即使用react-native link 命令。这应该可以链接我们刚刚安装的所有包的本地模块。但有时会出现错误,所以你必须检查软件包的文档,并查看其手动安装说明。

如果你使用的是React Native 0.60及以上版本,我们将不得不以不同的方式链接RNN、AsyncStorage和Vector Icons。

对于RNN,你可以通过在项目根目录下执行以下命令来完成。

npx rnn-link

对于AsyncStorage,你可以通过执行以下命令来实现(仍然是在项目的根目录下)。

npx pod-install

最后,对于矢量图标,对于Android应用程序,你必须导航到android 目录,对于iOS应用程序,导航到ios 目录。对于Android,编辑android/app/build.gradle 文件并在最后一个apply from 调用后添加以下内容。

apply from: "../../node_modules/react-native-vector-icons/fonts.gradle"

对于iOS,在ios 目录下执行以下命令。

pod install

最后,像这样更新index.js

import { Navigation } from "react-native-navigation";
import App from "./App";

Navigation.registerComponent('com.myApp.WelcomeScreen', () => App);
Navigation.events().registerAppLaunchedListener(() => {
   Navigation.setRoot({
     root: {
       stack: {
         children: [
           {
             component: {
               name: 'com.myApp.WelcomeScreen'
             }
           }
         ]
       }
     }
  });
});

试用该项目

在我们开始实际构建应用程序之前,让我们先试试这个项目,看看模块是否已经成功安装。首先,运行Metro Bundler。

npx react-native start

然后在任一平台上运行该应用程序。

npx react-native run-android
npx react-native run-ios

如果软件包没有问题,你应该可以看到默认的React Native项目欢迎界面。如果你看到这个屏幕,你现在可以继续构建应用程序。否则,请查看下面的常见问题部分来解决这个问题。

常见的问题

  1. 通常发生的第一个问题是本地模块的链接失败。这通常发生在RNN,因为他们有一个自定义脚本来链接本地模块。根据你所使用的React Native版本,这可能会失败。如果是这种情况,那就按照文档中的手动安装说明进行。
  1. 第二个常见的问题是这样的:"React Native multidex错误。.dex文件中的方法引用数量不能超过64K"。当应用程序(以及你所使用的库)超过一定数量的方法时,就会出现这种情况。在这种情况下,它大约是64,000个(确切地说,是65,536个)。这是Android构建架构的限制。为了解决这个问题,你可以启用multidex支持。要做到这一点,打开你的android/app/build.gradle 文件,在defaultConfigdependencies 下添加以下内容。
defaultConfig {
  // ...
  multiDexEnabled true
}
// ...
dependencies {
  // ...
  implementation 'com.android.support:multidex:1.0.3'
}

这些是你在尝试遵循本教程时可能遇到的两个最常见的问题。如果你遇到任何其他问题,请告诉我或搜索该问题。通常,有人已经遇到过这个问题,你可以在GitHub上的项目问题中找到这个问题。

构建应用程序

现在我们准备最终开始构建应用程序。

index.js

首先,打开项目目录根部的现有index.js ,用下面的代码替换其内容。这将作为应用程序的入口点。如果你注意到,我们不再需要使用React Native的AppRegistry来注册主要的应用程序组件。相反,我们现在使用RNN的registerComponent() 方法。这与我们之前对MainActivity.javaAppDelegate.m 文件所做的更新有关。

registerComponent() 方法接受屏幕的唯一名称和用来渲染屏幕的组件。一旦它被注册,我们就会调用registerAppLaunchedListener() 方法,将应用程序的根屏幕设置为LoadingScreen 。这与AppRegistry.registerComponent() 的作用类似。

// index.js
import { Navigation } from "react-native-navigation";
import Icon from "react-native-vector-icons/FontAwesome";
Icon.loadFont();

import Loading from "./src/screens/Loading"; // the loading screen

import "./loadIcons"; // file for loading the icons to be used in the bottom tab navigation

Navigation.registerComponent("LoadingScreen", () => Loading);

Navigation.events().registerAppLaunchedListener(() => {
  // set the root component
  Navigation.setRoot({
    root: {
      component: {
        name: "LoadingScreen",
      },
    },
  });
});

加载屏幕

加载屏幕作为应用程序的入口。但是你可能会问为什么要有一个加载屏幕?为什么不采用登录屏幕呢?这是因为我们的示例应用程序有一个模拟的登录系统,这意味着我们首先要确定用户是否已经登录。使用一个加载屏幕要比最初加载一个登录屏幕,却发现用户已经登录了,所以我们必须把他们导航到主屏幕,效果更好。

首先创建一个src/screens/Loading.js 文件并添加以下内容。

// src/screens/Loading.js
import React, { Component } from "react";
import { View, Text, ActivityIndicator, StyleSheet } from "react-native";

import { goToLogin, goToTabs } from "../../navigation"; // import the functions for loading either the login screen or the tabs screen (shows home screen by default)

import AsyncStorage from "@react-native-async-storage/async-storage";

接下来,创建组件本身。当组件被挂载时,我们尝试从本地存储中获取已登录用户的username 。如果它存在,我们就把用户导航到标签页,否则就导航到登录屏幕。

export default class Loading extends Component {
  async componentDidMount() {
    const username = await AsyncStorage.getItem("username");
    if (username) {
      goToTabs(global.icons, username);
    } else {
      goToLogin();
    }
  }

  render() {
    // show loading indicator
    return (
      <View style={styles.container}>
        <ActivityIndicator size="large" color="#0000ff" />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: "center",
    alignItems: "center",
  },
});

在上面的代码中,注意到我们把global.icons 作为一个参数传给了goToTabs() 函数。这个值被设置在我们先前从index.js 文件中导入的loadIcons.js 。它的工作是加载用于底部标签的图标,你将在后面看到。

navigation.js

这是我们注册应用程序所有屏幕的地方,并声明我们的导航函数,用于在登录屏幕和标签屏幕之间进行导航。

// navigation.js
import { Navigation } from "react-native-navigation";

import Login from "./src/screens/Login";
import ForgotPassword from "./src/screens/ForgotPassword";
import Home from "./src/screens/Home";
import Feed from "./src/screens/Feed";
import Gallery from "./src/screens/Gallery";

Navigation.registerComponent("LoginScreen", () => Login);
Navigation.registerComponent("ForgotPasswordScreen", () => ForgotPassword);
Navigation.registerComponent("HomeScreen", () => Home);
Navigation.registerComponent("FeedScreen", () => Feed);
Navigation.registerComponent("GalleryScreen", () => Gallery);

goToLogin() 函数创建一个堆栈导航。在RNN中,这些导航类型被称为 "Layouts"。目前,只有三种:堆栈、标签和抽屉。我们在本教程中只使用堆栈和标签,但下面是对每一种的简要介绍。

  • 堆栈:你导航到的每个新屏幕都是在当前屏幕的基础上布置的。因此,当你回到前一个屏幕时,其想法是简单地将当前屏幕从堆栈中 "弹 "出来。我们将使用堆栈导航在登录屏幕和忘记密码屏幕之间进行导航。
  • 标签:每个屏幕都可以通过底部的标签导航访问。每个标签上都有图标和文字来描述它所导航的屏幕。如果应用程序中有两个或更多的主屏幕,这种类型的导航通常被使用。有一个底部的标签导航允许在这些屏幕之间轻松访问。我们将使用标签导航在主页、画廊和Feed屏幕之间进行导航。
  • 抽屉:也叫侧面菜单。这被称为抽屉,因为它通常隐藏在汉堡包图标内,只有在点击时才会显示它下面的菜单。

回到代码中,我们只把登录屏幕作为堆栈导航的一个子项加入,尽管忘记密码屏幕也是它的一部分。正如前面提到的,我们将使用堆栈导航在登录界面和忘记密码界面之间进行导航。然而,我们在这里只把登录界面作为一个子界面加入。添加它将使它成为堆栈的默认屏幕。在一个堆栈导航中,你应该只为那个特定的堆栈添加初始屏幕作为子屏幕,正如你将在后面看到的。

对子屏幕的最低要求是为每个屏幕添加name 属性。这是用于渲染的屏幕的名称。这应该是你在注册组件时使用的相同名称。

export const goToLogin = () =>
  Navigation.setRoot({
    root: {
      stack: {
        // create a stack navigation
        id: "stackMain",
        children: [
          {
            component: {
              name: "LoginScreen",
            },
          },
        ],
      },
    },
  });

注意:为导航提供一个ID不是必须的,但这是很好的做法--特别是当你知道你将开始在你的应用程序中多次使用同一布局类型时。

接下来,添加goToTabs() 函数。与之前的函数不同,它接受两个参数:iconsusernameicons 是用于各个标签的图标数组,而username 是登录用户的用户名。这一次,我们要使用bottomTabs 导航。顾名思义,这允许用户使用底部标签在屏幕之间进行导航。你可以使用以下格式创建底部标签。

const iconColor = "#444";
const selectedIconColor = "#0089da";

export const goToTabs = (icons, username) => {
  Navigation.setRoot({
    root: {
      bottomTabs: {
        // create a bottom tabs navigation

        id: "bottomTabsMain",
        children: [
          {
            component: {
              name: "HomeScreen",
              options: {
                bottomTab: {
                  fontSize: 11,
                  text: "Home",
                  icon: icons[0],
                  iconColor,
                  selectedIconColor,
                },
              },

              // pass the username as a navigation prop to the Home screen
              passProps: {
                username,
              },
            },
          },

          {
            component: {
              name: "GalleryScreen",
              options: {
                bottomTab: {
                  fontSize: 11,
                  text: "Gallery",
                  icon: icons[1],
                  iconColor,
                  selectedIconColor,
                },
              },
            },
          },

          {
            component: {
              name: "FeedScreen",
              options: {
                bottomTab: {
                  fontSize: 11,
                  text: "Feed",
                  icon: icons[2],
                  iconColor,
                  selectedIconColor,
                },
              },
            },
          },
        ],
      },
    },
  });
};

正如你从上面的代码中看到的,这几乎使用了与堆栈导航相同的格式。唯一的区别是,这次我们还为各个bottomTab ,指定了一个options 属性。这些选项主要用于配置单个标签的样式。它们是不言自明的,所以我就不细说了,但我只想解释一下icon 这个属性。默认情况下,这接受了一个本地图片,这是由require('./path/to/image.png') 的调用所要求的。但既然我们已经安装了Vector Icons,我们不妨用它来代替图标源。唯一的问题是,我们不能真正提供一个React组件作为icon 的值,因为它需要一个资源。icons 参数接受一个图标资源的数组,这就是我们要使用的。你会在下一节了解到我们是如何加载这些资源的。

注意:你可以在造型的官方文档中找到更多的底部标签的造型选项。只要寻找bottomTabsbottomTab

继续阅读SitePoint上的React Native导航库入门