Skip to content

Latest commit

 

History

History
463 lines (347 loc) · 20 KB

File metadata and controls

463 lines (347 loc) · 20 KB

七、在 React 本机应用中使用NativeRouter

React 路由库提供了react-router-native包,其中包括用于 React 本机应用的NativeRouter组件的实现。React 原生框架允许您使用 JavaScript 和 React 为 iOS 和 Android 构建原生移动应用。

来自 React Native 的文档(https://facebook.github.io/react-native/

"With React Native, you don't build a mobile web app, an HTML5 app, or a hybrid app. You build a real mobile app that's indistinguishable from an app built using Objective-C or Java. React Native uses the same fundamental UI building blocks as regular iOS and Android apps. You just put those building blocks together using JavaScript and React."

本章讨论了以下主题:

  • 在 React 本机应用中使用 NativeRouter
  • 本地外部组件及其道具
  • 使用<BackButton>组件与设备的后退按钮交互
  • 使用<DeepLinking>组件创建深度链接

在 React 本机应用中使用 NativeRouter

create-react-appCLI 类似,create-react-native-appCLI 用于创建包含构建脚本的应用,该脚本可用于为开发和生产环境构建应用。它还包括packager,允许您在 iOS 和 Android 模拟器以及真实设备上测试应用。

使用 create react native app CLI 创建新项目

让我们首先安装 CLI:

npm install -g create-react-native-app

前面的命令将 CLI 安装在全局node_modules目录中。下一步是使用 CLI 创建 React 本机项目:

create-react-native-app react-native-test-app

创建react-native-test-app目录,并将所有需要的脚本下载到node_modules目录中

现在,当您运行npm start脚本时,构建脚本将启动packager,并生成一个 QR 码和 URL,供您在真实设备(iOS 或 Android)或模拟器上访问应用。此外,如果安装了 Xcode 或 Android Studio,则可以启动 iOS 或 Android emulator。下面是一个例子:

Your app is now running at URL: exp://192.168.1.100:19000
View your app with live reloading:
Android device:
-> Point the Expo app to the QR code above.
(You'll find the QR scanner on the Projects tab of the app.)
iOS device:
-> Press s to email/text the app URL to your phone.
Emulator:
-> Press a (Android) or i (iOS) to start an emulator.
Your phone will need to be on the same local network as this computer.
For links to install the Expo app, please visit https://expo.io.
Logs from serving your app will appear here. Press Ctrl+C at any time to stop.
 Press a to open Android device or emulator, or i to open iOS emulator.
 Press s to send the app URL to your phone number or email address
 Press q to display QR code.
 Press r to restart packager, or R to restart packager and clear cache.
 Press d to toggle development mode. (current mode: development)

在本例中,我们将使用 Xcode 模拟器;以下是当您请求在 iOS emulator 上查看应用时的应用屏幕截图:

React Native 提供了几个组件,允许您为本机平台构建视图。让我们看一下代码,了解用于构建前视图的一些组件。

App.js中包含以下代码:

export default class App extends React.Component {    
    render() {
        return (
            <View style={styles.container}>
                <Text>Open up App.js to start working on your app!</Text>
                <Text>Changes you make will automatically reload.</Text>
                <Text>Shake your phone to open the developer menu.</Text>
            </View>
        );
    }
}

这里,React Native 的<View>组件用于创建容器,其方式与在 React 应用中使用<div><section>创建容器的方式类似。在 React-Native 中,使用 React-Native 的组件,例如<View><Text>,而不是使用 HTML 元素,例如<div><span>

添加组件

现在,我们将react-router-native包添加到刚刚创建的应用中:

 npm install --save react-router-native

NativeRouter组件用于 React 本机应用中,以提供路由和导航支持。它允许在本机应用中使用<Route><Link>等组件

让我们首先创建一个包含两个<Link>组件的侧菜单:

import { Link } from 'react-router-native';

export class Menu extends Component {
    render() {
        return (
            <ScrollView scrollsToTop={false} style={styles.menu}>
                <View>
                    <Link to="/">
                        <Text>Home</Text>
                    </Link>
                    <Link to="/dashboard">
                        <Text>Dashboard</Text>
                    </Link>
                </View>
            </ScrollView>
        )
    }
}

<ScrollView>组件用作容器来承载我们的菜单项(<Link>组件)。顾名思义,<ScrollView>组件用于创建可滚动的容器。下一步是向应用添加<Route>组件:

export class ContentView extends Component {
    render() {
        return (
            <View style={styles.container}>
                <Route
                    path="/"
                    exact
                    component={HomeComponent}
                />
                <Route
                    path="/dashboard"
                    component={DashboardComponent} />
            </View>
        )
    }
}

ContentView组件将<Route>组件封装在<View>组件中,从而定义了两条路径为//dashboard的应用路由

最后一步,我们将使用react-native-side-menu中的<SideMenu>组件创建抽屉菜单。然后将此菜单包装在 App.js 中的<NativeRouter>组件中:

export default class App extends Component {
    render() {
        const menu = <Menu />;
        return (
            <NativeRouter>
                <View style={styles.container}>
                    <SideMenu menu={menu}>
                        <ContentView />
                    </SideMenu>
                </View>
            </NativeRouter>
        );
    }
}

与其他路由实现类似,NativeRouter组件包装应用根组件,并在用户浏览应用时启用<Route><Link>组件更新history

在仿真器上重建应用后:

当您选择其中一个链接时,ContentView将使用<Route>匹配后呈现的组件进行更新。

上述功能类似于BrowserRouter如何使您能够在应用中定义的各种路由中导航。与<Route><Link>组件类似,其他组件如<Switch><Redirect><NavLink>在本机应用中表现相同。但是,当您尝试使用<Prompt>组件阻止导航时,应使用 React Native 的Alert组件显示确认消息。

从 NativeRouter 的实现:

import { Alert } from "react-native";

NativeRouter.defaultProps = {
    getUserConfirmation: (message, callback) => {
        Alert.alert("Confirm", message, [
            { text: "Cancel", onPress: () => callback(false) },
            { text: "OK", onPress: () => callback(true) }
        ]);
    }
};

NativeRouter 提供了getUserConfirmation功能的默认实现,它利用react-native包中定义的Alert组件向用户显示确认消息:

此默认行为可以通过包含getUserConfirmation属性来覆盖:

<NativeRouter getUserConfirmation={customGetUserConfirmation}>
...
</NativeRouter>

组件

NativeRouter组件使用react-router包中定义的MemoryRouter组件在 React 本机应用中提供路由支持。MemoryRouter用于在内存中维护浏览历史,而不更新地址栏中的 URL。它在地址栏不可用的非浏览器环境中特别有用。MemoryRouter组件使用history包中可用的createMemoryHistory类创建history对象。然后将该history对象提供给低级<Router>接口。

NativeRotuer.js中:

import MemoryRouter from "react-router/MemoryRouter";

const NativeRouter = props => <MemoryRouter {...props} />;

然后MemoryRouter组件使用MemoryRouter.js中的createMemoryHistory创建history对象:

import { createMemoryHistory as createHistory } from "history";

class MemoryRouter extends React.Component {

    history = createHistory(this.props);
    ...

    render() {
        return <Router 
                  history={this.history} children={this.props.children}
               />;
    }
}

NativeRouter组件接受道具:initialEntriesinitialIndexgetUserConfirmationkeyLengthchildren。如前所述,getUserConfirmation的默认实现包含在NativeRouter类中,keyLengthchildren道具的行为与其他路由组件类似,如前几章所述。

让我们来看看 Pro T0 和 T1。

初始条目属性

initialEntries道具用于用位置列表填充历史堆栈:

export default class App extends Component {
    render() {
        const initialEntries = ['/', '/dashboard'];

        return (
            <NativeRouter initialEntries={initialEntries}>
            ...
            </NativeRouter>
        );
    }
}

在初始化NativeRouter时,您可以通过提供位置路径数组来填充历史记录。位置路径可以是字符串,甚至是形状为{ pathname, search, hash, state }的对象:

const initialEntries = [
    '/' ,
    { 
 pathname: '/dashboard',
 search: '',
 hash: 'test', 
 state: { from: '/'}
 }
];

初始索引道具

initialIndex属性用于指定应用加载时要渲染的initialEntries数组中位置的索引值。例如,如果initialEntries数组列出了两个位置,则initialIndex1加载第二个条目;也就是说,呈现一个与initialEntries数组中提到的第二个条目路径名匹配的<Route>实例:

export default class App extends Component {
    render() {
        const initialEntries = ['/', '/dashboard'];
        const initialIndex = 1;

        return (
            <NativeRouter 
                initialEntries={initialEntries} 
                initialIndex={initialIndex}>
            ...
            </NativeRouter>
        )
    }
}

在本例中,initialIndex值被设置为1,因此在应用加载时呈现与位置路径/dashboard匹配的<Route>

组件

默认情况下,当您在 Android 设备上按下后退按钮时,应用将退出,而不是将用户导航到历史记录中的上一个状态。React 本机库包含一个BackHandler类,可用于自定义设备的硬件后退按钮的行为。React Router 中的<BackButton>组件使用BackHandler类定制 Android 设备上后退按钮的行为:

import { NativeRouter, BackButton } from 'react-router-native';

export default class App extends Component {
    render() {
        return (
            <NativeRouter>
                <View style={styles.container}>
                    <BackButton />
                    <SideMenu menu={menu}>
                        <ContentView />
                    </SideMenu>
                </View>
            </NativeRouter>
        )
    }
}

<BackButton>组件可以包含在应用中的任何位置。在前面的示例中,该组件包含在根组件中,而不包含任何子组件。请注意,<BackButton>组件不会在视口中渲染任何内容;相反,它有助于与设备的后退按钮进行交互。

以下是工作流程:

在仪表板屏幕上(路径/dashboard处),当您单击设备的后退按钮时,用户将被导航到主页(路径/

与建立深度链接

在 web 应用中,HTTP URL 指的是可以通过在浏览器的地址栏中输入相同地址来访问的位置。在单页应用中,此位置指用户可以导航到的特定路由。在移动应用的上下文中,DeepLink指您想要查看的特定页面或内容。例如,当您单击移动设备上的链接时,将启动应用并显示请求的页面,而不是在浏览器窗口中打开新选项卡。

与使用 HTTP 引用特定位置的 web 应用不同,移动设备上的应用需要为应用声明 URI 方案。例如,Twitter 应用使用 URI 方案twitter://,因此您可以通过参考 URItwitter://profile来查看他们的 Twitter 配置文件。当用户单击电子邮件中的链接或访问推送通知消息时,深度链接非常有帮助,推送通知消息将用户导航到应用以显示请求的内容。

React Native 提供的接口允许您为 iOS 和 Android 平台上的设备创建深度链接。在本节中,我们将了解如何在 Android 设备上创建指向应用内容的深度链接,因此我们需要安装 Android Studio。Android Studio 允许我们创建虚拟设备(AVD),然后用于测试深度链接。

A step-by-step guide to installing the necessary components on iOS and Android is detailed in the React Native documentation: https://facebook.github.io/react-native/docs/getting-started.html.

在安装 Android Studio 并创建 AVD 之后,应用需要配置 URI 方案。要添加 URI 方案,需要更新一些本机文件,要访问这些本机文件,需要从当前设置中弹出。

正在从创建本地应用中弹出

create-react-native-appCLI 是构建 React 本机应用和在模拟器上测试应用的非常好的选项。但是,为了测试DeepLinking,我们需要在清单文件中包含条目,因此需要使用以下命令从配置中弹出:

npm run eject

前面的命令将生成 iOS 和 Android 平台的配置文件。这个简单的最低配置允许您为 iOS 设备生成一个.ipa文件,为 Android 设备生成一个.apk文件。在本节中,我们将看到如何生成.apk文件,然后将其部署到 AVD。

弹出后,您将看到为 iOS 和 Android 生成的各种目录和文件:

|--/android
|----/.gradle
|----/app
|----/build
|----/gradle
|----/keystores
|--/ios
|----/chapter7DeepLink
|----/chapter7DeepLink-tvOS
|----/chapter7DeepLink-tvOSTests
|----/chapter7DeepLink.Xcodeproj
|----/chapter7DeepLinkTests

下一步是在 Android 设备上构建并运行应用:

npm run android

前面的命令将运行构建脚本并生成.apk文件,该文件将部署在 AVD 上。请确保在执行上一个命令之前已运行虚拟设备。

要在 Android 设备上配置 URI 方案,需要更新位于/android/app/src/main路径的AndroidManifest.xml清单文件。在下一节中,我们将看到需要添加到清单文件的配置。

将添加到清单文件

AndroidManifest.xml文件包含有关应用的元信息,用于声明应用中存在的各种组件。使用意向过滤器激活这些组件。清单文件中的<intent-filter>实例用于定义应用的功能,以及定义其他应用如何与应用交互的策略。

从配置中弹出时,生成AndroidManifest.xml文件:

<manifest 
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.chapter7deeplink">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

    <application
        android:name=".MainApplication"
        android:label="@string/app_name"
        android:icon="@mipmap/ic_launcher"
        android:allowBackup="false"
        android:theme="@style/AppTheme">
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"

       android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
            android:windowSoftInputMode="adjustResize">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity 
       android:name="com.facebook.react.devsupport.DevSettingsActivity" />
    </application>
</manifest>

这里,<intent-filter>将应用的动作和类别定义为android.intent.action.MAINandroid.intent.category.LAUNCHER。前面的intent-filter允许在用户设备上查看应用,当用户点击应用时,会触发应用中的MainActivity(参见活动标签)

类似地,可以将为应用定义 URI 方案的intent-filter添加到清单文件中:

<intent-filter android:label="filter_react_native">
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data android:scheme="deeplink" android:host="app.chapter7.com" />
</intent-filter>

这里,<data>标记用于指定应用的 URI 方案。<data>标签中的android:scheme属性用于指定方案名称,android:host属性用于指定应用使用的hostname类型。因此,deeplink://app.chapter7.comURI 用于访问应用中的主页。可以使用以下 URI 访问具有/dashboard路径的路由:deeplink://app.chapter7.com/dashboard

下一步是使用 React Router 的<DeepLinking>组件,以便应用能够对传入的请求做出反应,并将用户导航到请求路由。

包括组件

react-router-native包中的<DeepLinking>组件使用 React Native 的Linking接口监听 URL 中的更改。无论何时检测到更改,都会通过在历史堆栈中添加条目将用户导航到请求的路径。

<DeepLinking>组件可以包含在应用中的任何位置:

export class RootComponent extends Component {
    render() {
        return (
            <View style={styles.container}>
                <DeepLinking />
                <View style={styles.nav}>
                    <Link to="/app.chapter7.com">
                        <Text>Home</Text>
                    </Link>
                    <Link to="/app.chapter7.com/dashboard">
                        <Text>Dashboard</Text>
                    </Link>
                </View>
                <View style={styles.routeContainer}>
                    <Route path="/app.chapter7.com" exact component={HomeComponent} />
                    <Route path="/app.chapter7.com/dashboard" component={DashboardComponent} />
                </View>
            </View>
        )
    }
}

在这里,<DeepLinking>组件包含在应用的RootComponent中,并且<Route>路径更新为前缀app.chapter7.com,以匹配AndroidManifest.xml文件中声明的主机名。

要测试深度链接,请尝试以下命令:

adb shell am start -W -a android.intent.action.VIEW -d deeplink://app.chapter7.com/dashboard

上一个命令应在 AVD 上启动应用,并将您导航到具有/dashboard路径的路由。

总结

在本章中,我们了解了如何在 React 本机应用中使用 React 路由的<NativeRouter>组件。<NativeRouter>组件在react-router-native包中提供,内部使用react-router包中定义的<MemoryRouter>组件。<NativeRouter>组件接受道具:initialEntriesinitialIndexgetUserConfirmationkeyLengthchildren。此外,它还为getUserConfirmation函数提供了一个默认实现,该函数使用 React Native 的Alert组件来显示确认消息。当<Prompt>组件包含在应用中,并且用户试图导航离开当前路由时,将显示此确认消息。

react-router-native中的<BackButton>组件是 React Native 的BackHandler类的包装器,它侦听设备的“后退”按钮,并通过历史堆栈中的一个条目将用户导航回去。<DeepLinking>组件用于处理应用中内容的深层链接。该组件使用 React Native 的Linking接口监听 URL 更改,并在使用深度链接 URI 方案访问应用时将用户导航到请求的路由。要为应用定义 URI 方案,AndroidManifest.xml清单文件将更新为主活动(.MainActivity<intent-filter>intent-filter声明用于访问应用内部内容的 URI 方案和主机名。

在下一章中,我们将了解状态管理工具 Redux,并了解 React Router 如何与 Redux 结合使用。