1

深度链接是一种技术,其中给定的 URL 或资源用于在移动设备上打开特定页面或屏幕。因此,深度链接可以引导用户到应用程序内的特定屏幕,而不仅仅是启动移动设备上的应用程序,从而提供更好的用户体验。这个特定的屏幕可能位于一系列层次结构页面下,因此称之为"深度"链接。

它对于营销活动,应用用户保留等非常有用。作为一个应用用户,你可能在打开链接时体验过深度链接,例如,从网页浏览器打开电子商务商店中的产品链接。如果你已经安装了该商店的应用,它可能会使用深度链接来打开应用,并直接导航你到该产品的屏幕。

在本教程中,让我们通过创建一个示例应用来学习如何在 React Native 应用中处理深度链接。我们将创建一个简单的应用来处理深度链接,并通过使用 React Navigation 库来配置深度链接。

可以在此 GitHub 仓库中找到本教程的完整代码。

深度链接配置 React Native 应用中的导航

我们从创建一个新的 React Native 应用程序开始。首先,打开终端并运行以下命令:

npx react-native init rnDeepLinking

# after the project is generated by the above command
# navigate to the rnDeepLinking directory
cd rnDeepLinking

在本教程中,您将构建的示例应用将包含两个屏幕。第一个屏幕将是带有物品列表的主屏幕。第二个屏幕将是显示物品详细信息的详细信息屏幕。

我们配置 React Navigation 版本 6 并安装所需的依赖项。这将允许通过导航配置深度链接,并在两个屏幕之间进行导航。

yarn add @react-navigation/native @react-navigation/native-stack react-native-screens react-native-safe-area-context

下一步是链接您刚刚安装的所有库。此示例应用程序使用的是 0.67.x 版本的 React Native。

在 iOS 设备上,必须运行以下一组命令。

npx pod-install ios

对于 Android,打开文件 android/app/src/main/java/<您的 React Native 项目名称>/MainActivity.java 并添加以下代码片段:

package com.rndeeplinking;

import android.os.Bundle;
import com.facebook.react.ReactActivity;

public class MainActivity extends ReactActivity {

  /**
   * Returns the name of the main component registered from JavaScript. This is used to schedule
   * rendering of the component.
   */
  @Override
  protected String getMainComponentName() {
    return "rnDeepLinking";
  }
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(null);
  }
}

这就是在一个裸露的 React Native 应用中配置 React Navigation 库所需的全部内容。

注意:在裸露的 React Native 项目中配置 React Navigation 库的过程可能会改变。建议按照官方文档的指示进行操作。

创建主页和详情屏幕

创建一个名为 src/screens 的新目录。这将包含应用程序的所有屏幕组件。在其中,创建两个新文件:HomeScreen.jsDetailsScreen.js

HomeScreen.js 文件从 Json 占位符 API 的模拟数据数组中显示一份人员名单。该列表使用 React Native 的 FlatList 组件进行渲染。

每个列表中的人都被 Pressable 组件包裹,这样当应用用户按下列表中的用户名称时,他们将导航到详情屏幕。

// src/screens/HomeScreen.js

import React, { useState, useEffect } from 'react';
import {
  ActivityIndicator,
  View,
  Text,
  FlatList,
  Pressable
} from 'react-native';

import Separator from '../components/Separator';

const HomeScreen = ({ navigation }) => {
  const [data, setData] = useState([]);
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    fetch('https://jsonplaceholder.typicode.com/users')
      .then(res => res.json())
      .then(res => {
        setData(res);
        setIsLoading(false);
      })
      .catch(error => {
        console.log(error);
      });
  }, []);

  const renderList = ({ item }) => {
    return (
      <Pressable
        onPress={() => alert('Navigate to Details screen')}
        style={{ paddingHorizontal: 10 }}
      >
        <Text style={{ fontSize: 24, color: '#000' }}>{item.name}</Text>
      </Pressable>
    );
  };

  return (
    <View style={{ flex: 1 }}>
      {isLoading ? (
        <ActivityIndicator color="blue" size="large" />
      ) : (
        <>
          <FlatList
            data={data}
            contentContainerStyle={{
              paddingVertical: 20
            }}
            keyExtractor={item => item.id}
            ItemSeparatorComponent={Separator}
            renderItem={renderList}
          />
        </>
      )}
    </View>
  );
};

export default HomeScreen;

我们也在 src/components 目录中创建一个新文件,命名为 Separator.js。这个文件包含一个<Separator />组件,用于在 HomeScreen 中划分列表项。 <Separator />组件是一个带有一些附加样式的简单视图。

它被用作 FlatList 组件中 prop ItemSeparatorComponent 的值。 ItemSeparatorComponent prop 定义了一个自定义分隔符,并在列表中的每个项目之间渲染。

/ src/components/Separator.js

import React from 'react';
import { View } from 'react-native';

const Separator = () => (
  <View
    style={{
      borderBottomColor: '#d3d3d3',
      borderBottomWidth: 1,
      marginTop: 10,
      marginBottom: 10
    }}
  />
);

export default Separator;

对于详细信息屏幕,现在,让我们只在屏幕组件文件 DetailsScreen.js 中显示一个文本字符串:

import React from 'react';
import { View, Text } from 'react-native';

const DetailsScreen = ({ navigation }) => {
  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text>Details Screen</Text>
    </View>
  );
};

export default DetailsScreen;

设置堆栈导航器

要在应用中设置堆栈导航器,请创建一个名为 src/navigation/RootNavigator.js 的新文件,并添加以下代码片段:

/ src/navigation/RootNavigator.js

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

import HomeScreen from '../screens/HomeScreen';
import DetailsScreen from '../screens/DetailsScreen';

const RootStack = createNativeStackNavigator();

const RootNavigator = () => {
  return (
    <NavigationContainer>
      <RootStack.Navigator>
        <RootStack.Screen name="Home" component={HomeScreen} />
        <RootStack.Screen name="Details" component={DetailsScreen} />
      </RootStack.Navigator>
    </NavigationContainer>
  );
};

export default RootNavigator;

然后,在 App.js 文件中导入 RootNavigator

// App.js

import React from 'react';

import RootNavigator from './src/navigation/RootNavigator';

const App = () => {
  return <RootNavigator />;
};

export default App;

要构建并运行该应用,打开两个终端窗口实例。在第一个实例中,运行 npx react-native start。这将启动 React Native 打包器。

要为 iOS 或 Android 构建应用程序,请在终端窗口的第二个实例中运行适当的命令。这将为你指定的平台构建应用程序。

# for iOS
npx react-native run-ios

# for android
npx react-native run-android

一旦应用程序构建完成,上述命令将会在指定平台上安装它。以下是该应用程序在 iOS 模拟器和真实 Android 设备上运行的示例:

image.png

在 React Navigation 中配置深度链接

在 React Native 应用程序中处理深度链接有两种方法:

大多数生产级应用程序都有多个屏幕和嵌套的导航器。所以,让我们看看如何在我们的示例应用中使用 React Navigation 来实现它。

要允许 React Navigation 库通过其路由逻辑处理深度链接,需要定义一个配置对象。在此对象中,定义一个包含 URI 方案的前缀属性。应用程序是基于此 URI 方案打开的。

然后,将此配置对象传递给 NavigationContainer 上名为 linkingprop。另外,在容器上添加一个 fallback prop。它将渲染并显示加载指示器,直到深层链接被解析。

// src/navigation/RootNavigator.js

// rest of the import statement remains same
import { ActivityIndicator } from 'react-native';

const linking = {
  prefixes: ['peoplesapp://']
};

const RootNavigator = () => {
  return (
    <NavigationContainer
      linking={linking}
      fallback={<ActivityIndicator color="blue" size="large" />}
    >
      <RootStack.Navigator>
        <RootStack.Screen name="Home" component={HomeScreen} />
        <RootStack.Screen name="Details" component={DetailsScreen} />
      </RootStack.Navigator>
    </NavigationContainer>
  );
};

使用 URI-scheme 包来配置 URI 方案

而不是手动设置 iOS 和 Android 的 URI 方案,可以使用 uri-scheme npm 包。它允许在 iOS 和 Android 设备上配置和测试本地 URI 方案。

注意:如果您想深入研究并手动为 iOS 和 Android 设置 URI 方案,请查看接下来的两个部分。

要设置该方案,请为适当的平台运行以下命令:

# for iOS
npx uri-scheme add peoplesapp --ios

# for Android
npx uri-scheme add peoplesapp --android

完成此步骤后,请确保使用 npx react-native run-ios 或 npx react-native run-android 再次为特定平台构建应用程序。

iOS 配置方案

要手动为 iOS 设备设置方案,请打开 ios/your-project-name/AppDelegate.m 文件并添加以下代码片段:

/ Add the header at the top of the file:
#import <React/RCTLinkingManager.h>

// Add this above `@end`:
- (BOOL)application:(UIApplication *)application
   openURL:(NSURL *)url
   options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
  return [RCTLinkingManager application:application openURL:url options:options];
}

现在,让我们将 URI 方案添加到 iOS 项目配置中。打开 Xcode 中的 Your-app-name/ios/app-name.xcworkspace

然后,在左侧边栏中选择项目名称,然后导航到信息选项卡:

image.png

接下来,转到 URL Types,点击+(加号)按钮,在标识符和 URL 方案下,添加 peoplesapp

image.png

URL Types 与 web URL 中的 http 表示的类似。这是 iOS 用来打开应用程序的内容。

在此配置步骤之后,使用 npx react-native run-ios 重新构建您的 iOS 应用程序。

为 Android 配置方案

要为 Android 设备手动设置方案,您需要配置该方案。打开/android/app/src/main/AndroidManifest.xml 并将 launchMode 的值设置为 singleTask。要添加方案,请按照下面的示例添加一个新的 intent-filter 标签:

<!-- Set the launchMode to singleTask in <activity> -->
<activity
  android:name=".MainActivity"
  android:label="@string/app_name"
  android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
  android:launchMode="singleTask"
  android:windowSoftInputMode="adjustResize">
  <intent-filter>
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />
  </intent-filter>
  <!-- Add this new intent-filter tag -->
  <!-- Make sure to set the value of android:scheme to your own scheme -->
  <intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data android:scheme="peoplesapp" />
  </intent-filter>
</activity
>

在此配置步骤之后,使用 npx react-native run-android 重新构建您的 Android 应用。

测试 iOS 应用程序

然后,在终端窗口中,运行以下命令:

# replace peoplesapp:// with your own URL
xcrun simctl openurl booted peoplesapp://

# OR use uri-scheme package to test
npx uri-scheme open peoplesapp:// --ios

这将打开示例应用程序:

也可以通过在模拟器设备上打开网络浏览器并运行 URL peoplesapp://来测试它。它将询问您是否打开外部 URI,如下所示:

测试安卓应用程序

为了测试到目前为止的配置设置,我正在使用一个真实的 Android 设备。您也可以使用 Android 模拟器。在测试之前,请确保如果示例应用程序已经在运行,要关闭它。

从终端窗口,运行以下命令:

# replace peoplesapp:// with your own URL
adb shell am start -W -a android.intent.action.VIEW -d "peoplesapp://"

# OR use uri-scheme package to test
npx uri-scheme open peoplesapp:// --android

运行上述命令后的输出如下:

嵌套屏幕配置

要在访问具有 URL 方案的详细信息屏幕时显示每个人的信息,必须配置详细信息屏幕的路径,并添加一个代表列表中人员 id 的动态参数。

const linking = {
  prefixes: ['peoplesapp://'],
  config: {
    initialRouteName: 'Home',
    screens: {
      Home: {
        path: 'home'
      },
      Details: {
        path: 'details/:personId'
      }
    }
  }
}

personId 现在可以作为路由参数在详情屏幕上使用。路由参数可以通过 React Navigation 库的 route.params 在屏幕上访问。

根据 personId 的值,详情屏幕将从 API 获取数据并显示该人的信息。

我们也处理一种情况,即应用程序用户从主屏幕导航到详情屏幕,也就是说,不使用链接。在这种情况下,打开 HomeScreen.js 并替换 Pressable 组件上的 onPress 属性值,如下所示:


<Pressable
  onPress={() => navigation.navigate('Details', { personDetailsId: item.id })}
  style={{ paddingHorizontal: 10 }}
>
  <Text style={{ fontSize: 24, color: '#000' }}>{item.name}</Text>
</Pressable>

请注意,personDetailsId 是传递给上述片段中的 Details 屏幕的路由参数。只有当用户从主屏幕导航到 Details 屏幕时,才会获取某人的详细信息。

在详细信息屏幕中,我们从 route.params 对象中获取 personDetailsId(来自主屏幕的 id)和 personId(从 URL 方案中使用的 id)。

然后使用 useEffect 钩子,从 Json Placeholder API 获取数据并渲染详细信息:

import React, { useState, useEffect } from 'react';
import { View, Text, ActivityIndicator } from 'react-native';

const DetailsScreen = ({ route }) => {
  const params = route.params || {};
  const { personDetailsId, personId } = params;

  const [data, setData] = useState([]);
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    if (personId) {
      fetch(`https://jsonplaceholder.typicode.com/users/${personId}`)
        .then(res => res.json())
        .then(res => {
          const fetchedDetails = [];

          Object.keys(res).forEach(key => {
            fetchedDetails.push({ key, value: `${res[key]}` });
          });
          setData(fetchedDetails);
          setIsLoading(false);
        })
        .catch(error => {
          console.log(error);
        });
    } else {
      fetch(`https://jsonplaceholder.typicode.com/users/${personDetailsId}`)
        .then(res => res.json())
        .then(res => {
          const fetchedDetails = [];

          Object.keys(res).forEach(key => {
            fetchedDetails.push({ key, value: `${res[key]}` });
          });

          setData(fetchedDetails);
          setIsLoading(false);
        })
        .catch(error => {
          console.log(error);
        });
    }
  }, []);

  return (
    <View style={{ flex: 1 }}>
      {isLoading ? (
        <ActivityIndicator color="blue" size="large" />
      ) : (
        <View style={{ paddingTop: 10, paddingHorizontal: 10 }}>
          {data.map(person => (
            <Text
              style={{ fontSize: 24, paddingBottom: 2 }}
              key={person.key}
            >{`${person.key}: ${person.value}`}</Text>
          ))}
        </View>
      )}
    </View>
  );
};

export default DetailsScreen;

通过从列表中按下某人的名字从主页导航到详细信息屏幕时,这是输出:

使用 URL 方案时的输出如下:

总结

现在已经完成了一个使用 React Navigation 库处理深度链接的 React Native 应用程序的演示

深度链接可以显著改善您的移动应用的用户体验,并使搜索引擎提供上下文敏感的搜索和结果。希望这个指南能帮助你在你的应用中取得优秀的成果。

首发于公众号 大迁世界,欢迎关注。📝 每周一篇实用的前端文章 🛠️ 分享值得关注的开发工具 ❓ 有疑问?我来回答

本文 GitHub https://github.com/qq449245884/xiaozhi 已收录,有一线大厂面试完整考点、资料以及我的系列文章。


王大冶
68k 声望104.9k 粉丝