跳到主要内容
版本:7.x

配置链接

在本指南中,我们将配置 React Navigation 来处理外部链接。如果您想要执行以下操作,这是必要的:

  1. 处理 Android 和 iOS 上的 React Native 应用中的深度链接
  2. 在使用 Web 时启用浏览器中的 URL 集成
  3. 使用 <Link />useLinkTo 使用路径进行导航。

在继续之前,请确保您已在您的应用中配置了深度链接。如果您有 Android 或 iOS 应用,请记住指定 prefixes 选项。

Navigation 组件接受一个 linking 属性,该属性使其更容易处理传入链接

import { createStaticNavigation } from '@react-navigation/native';

const linking = {
enabled: 'auto' /* Automatically generate paths for all screens */,
prefixes: [
/* your linking prefixes */
],
};

function App() {
return (
<Navigation
linking={linking}
fallback={<Text>Loading...</Text>}
/>
);
}

const Navigation = createStaticNavigation(RootStack);

当您指定 linking 属性时,React Navigation 将自动处理传入链接。在 Android 和 iOS 上,它将使用 React Native 的 Linking 模块 来处理传入链接,包括当应用使用链接打开时,以及当应用打开时收到新链接时。在 Web 上,它将使用 History API 来将 URL 与浏览器同步。

警告

目前似乎存在一个错误 (facebook/react-native#25675),导致它在 Android 上永远无法解决。我们添加了一个超时以避免永远卡住,但这意味着链接在某些情况下可能无法处理。

您还可以传递一个 fallback 属性,该属性控制当 React Navigation 尝试解析初始深度链接 URL 时显示的内容。

前缀

prefixes 选项可用于指定自定义方案(例如 mychat://),以及主机名和域名(例如 https://mychat.com),如果您已配置 通用链接Android App Links

例如

const linking = {
prefixes: ['mychat://', 'https://mychat.com'],
};

请注意,Web 不支持 prefixes 选项。主机名和域名将自动从浏览器中的网站 URL 确定。如果您的应用仅在 Web 上运行,则可以从配置中省略此选项。

多个子域名​

要匹配关联域的所有子域名,您可以在特定域的开头添加 * 通配符。请注意,*.mychat.com 的条目不匹配 mychat.com,因为星号后有点号。要同时启用 *.mychat.commychat.com 的匹配,您需要为每个提供单独的前缀条目。

const linking = {
prefixes: ['mychat://', 'https://mychat.com', 'https://*.mychat.com'],
};

过滤特定路径

有时我们可能不想处理所有传入链接。例如,我们可能想要过滤掉用于身份验证(例如 expo-auth-session)或其他目的的链接,而不是导航到特定屏幕。

为了实现这一点,您可以使用 filter 选项

const linking = {
prefixes: ['mychat://', 'https://mychat.com'],
filter: (url) => !url.includes('+expo-auth-session'),
};

Web 不支持此功能,因为我们始终需要处理页面的 URL。

子路径下的应用

如果您的应用托管在子路径下,则可以在 config 的顶层指定子路径。例如,如果您的应用托管在 https://mychat.com/app,您可以将 path 指定为 app

const linking = {
prefixes: ['mychat://', 'https://mychat.com'],
config: {
path: 'app',

// ...
},
};

此处无法指定参数,因为这不属于屏幕,例如 app/:id 将不起作用。

将路径映射到路由名称

如果您在 linking 属性中指定 enabled: 'auto',React Navigation 将自动为所有屏幕生成路径。例如,如果您在导航器中有一个 Profile 屏幕,它将自动为其生成一个路径,如 profile

如果您希望手动处理配置,或者想要覆盖特定屏幕的生成路径,您可以在导航器中的屏幕旁边指定 linking 属性,以将路径映射到屏幕。例如

const RootStack = createStackNavigator({
screens: {
Profile: {
screen: ProfileScreen,
linking: {
path: 'user',
},
},
Chat: {
screen: ChatScreen,
linking: {
path: 'feed/:sort',
},
},
},
});

在此示例中

  • Chat 屏幕处理带有参数 sort 的 URL /feed(例如 /feed/latest - Chat 屏幕将接收一个参数 sort,其值为 latest)。
  • Profile 屏幕处理 URL /user

同样,当您有嵌套导航器时,您可以在导航器中的屏幕上指定 linking 属性,以处理嵌套屏幕的路径

const HomeTabs = createBottomTabNavigator({
screens: {
Home: {
screen: HomeScreen,
linking: {
path: 'home',
},
},
Settings: {
screen: SettingsScreen,
linking: {
path: 'settings',
},
},
},
});

const RootStack = createStackNavigator({
screens: {
HomeTabs: {
screen: HomeTabs,
},
Profile: {
screen: ProfileScreen,
linking: {
path: 'user',
},
},
Chat: {
screen: ChatScreen,
linking: {
path: 'feed/:sort',
},
},
},
});

在上面的示例中,处理以下路径格式

  • /home 导航到 HomeTabs -> Home 屏幕
  • /settings 导航到 HomeTabs -> Settings 屏幕
  • /user 导航到 Profile 屏幕
  • /feed/:sort 导航到带有参数 sortChat 屏幕

自动路径生成如何工作?

当使用 enabled: 'auto' 进行自动路径生成时,应用以下规则

  • 具有显式 linking 属性的屏幕不用于路径生成,并将按原样添加。
  • 屏幕名称将从 PascalCase 转换为 kebab-case 以用作路径(例如 NewsFeed -> news-feed)。
  • 除非屏幕具有用于主页的显式空路径 (path: ''),否则遇到的第一个叶子屏幕将用作主页。
  • 路径生成仅处理叶子屏幕,即不会为包含嵌套导航器的屏幕生成路径。仍然可以使用显式 linking 属性为其指定路径。

假设我们有以下导航结构

const HomeTabs = createBottomTabNavigator({
screens: {
Home: {
screen: HomeScreen,
},
Settings: {
screen: SettingsScreen,
},
},
});

const RootStack = createStackNavigator({
screens: {
HomeTabs: {
screen: HomeTabs,
},
Profile: {
screen: ProfileScreen,
},
Chat: {
screen: ChatScreen,
},
},
});

使用自动路径生成,将生成以下路径

  • / 导航到 HomeTabs -> Home 屏幕
  • /settings 导航到 HomeTabs -> Settings 屏幕
  • /profile 导航到 Profile 屏幕
  • /chat 导航到 Chat 屏幕

如果 URL 包含查询字符串,它将作为参数传递给屏幕。例如,URL /profile?user=jane 将把 user 参数传递给 Profile 屏幕。

它是如何工作的

链接的工作原理是使用提供的配置将 URL 转换为有效的 导航状态,反之亦然。例如,路径 /rooms/chat?user=jane 可能会转换为如下所示的状态对象

const state = {
routes: [
{
name: 'rooms',
state: {
routes: [
{
name: 'chat',
params: { user: 'jane' },
},
],
},
},
],
};

例如,您可能想要将路径 /feed/latest 解析为类似这样的内容

const state = {
routes: [
{
name: 'Chat',
params: {
sort: 'latest',
},
},
];
}

有关状态对象结构的更多详细信息,请参阅 导航状态参考

传递参数

一个常见的用例是向屏幕传递参数以传递一些数据。例如,您可能希望 Profile 屏幕具有一个 id 参数,以了解它是哪个用户的个人资料。在处理深度链接时,可以通过 URL 将参数传递给屏幕。

默认情况下,查询参数被解析以获取屏幕的参数。例如,在上面的示例中,URL /user?id=jane 将把 id 参数传递给 Profile 屏幕。

您还可以自定义如何从 URL 解析参数。假设您希望 URL 看起来像 /user/jane,其中 id 参数是 jane,而不是在查询参数中包含 id。您可以通过为 path 指定 user/:id 来做到这一点。当路径段以 : 开头时,它将被视为参数。例如,URL /user/jane 将解析为 Profile 屏幕,字符串 jane 作为 id 参数的值,并且在 Profile 屏幕的 route.params.id 中可用。

默认情况下,所有参数都视为字符串。您还可以通过在 parse 属性中指定一个函数来解析参数,并在 stringify 属性中指定一个函数将其转换回字符串,从而自定义如何解析它们。

如果您想将 /user/@jane/settings 解析为参数 { id: 'jane' section: 'settings' },您可以使 Profile 的配置如下所示

const RootStack = createStackNavigator({
screens: {
Profile: {
screen: ProfileScreen,
linking: {
path: 'user/:id/:section',
parse: {
id: (id) => id.replace(/^@/, ''),
},
stringify: {
id: (id) => `@${id}`,
},
},
},
},
});
结果导航状态

使用此配置,路径 /user/@jane/settings 将解析为以下状态对象

const state = {
routes: [
{
name: 'Profile',
params: { id: 'jane', section: 'settings' },
},
],
};

将参数标记为可选

有时,参数可能存在也可能不存在于 URL 中,具体取决于某些条件。例如,在上述场景中,您可能并非总是在 URL 中包含 section 参数,即 /user/jane/settings/user/jane 都应该转到 Profile 屏幕,但 section 参数(在本例中值为 settings)可能存在也可能不存在。

在这种情况下,您需要将 section 参数标记为可选。您可以通过在参数名称后添加 ? 后缀来完成此操作

const RootStack = createStackNavigator({
screens: {
Profile: {
screen: ProfileScreen,
linking: {
path: 'user/:id/:section?',
parse: {
id: (id) => `user-${id}`,
},
stringify: {
id: (id) => id.replace(/^user-/, ''),
},
},
},
},
});
结果导航状态

使用此配置,路径 /user/jane 将解析为以下状态对象

const state = {
routes: [
{
name: 'Profile',
params: { id: 'user-jane' },
},
],
};

如果 URL 包含 section 参数(例如 /user/jane/settings),这将导致以下结果,配置相同

const state = {
routes: [
{
name: 'Profile',
params: { id: 'user-jane', section: 'settings' },
},
],
};

处理不匹配的路由或 404

如果您的应用使用无效的 URL 打开,大多数情况下您都希望显示一个包含一些信息的错误页面。在 Web 上,这通常称为 404 - 或页面未找到错误。

要处理这种情况,您需要定义一个 catch-all 路由,如果没有其他路由匹配路径,则将呈现该路由。您可以通过为路径匹配模式指定 * 来完成此操作

const HomeTabs = createBottomTabNavigator({
screens: {
Feed: {
screen: FeedScreen,
},
Profile: {
screen: HomeScreen,
linking: {
path: 'users/:id',
},
},
Settings: {
screen: SettingsScreen,
linking: {
path: 'settings',
},
},
},
});

const RootStack = createStackNavigator({
screens: {
Home: {
screen: HomeTabs,
},
NotFound: {
screen: NotFoundScreen,
linking: {
path: '*',
},
},
},
});

在这里,我们定义了一个名为 NotFound 的路由,并将其设置为匹配 *,即所有内容。如果路径与 user/:idsettings 不匹配,则将由此路由匹配。

结果导航状态

使用此配置,诸如 /library/settings/notification 之类的路径将解析为以下状态对象

const state = {
routes: [{ name: 'NotFound' }],
};

您甚至可以更具体,例如,假设您想要为 /settings 下的无效路径显示不同的屏幕,您可以在 Settings 下指定这样的模式

const SettingsStack = createStackNavigator({
screens: {
UserSettings: {
screen: UserSettingsScreen,
linking: {
path: 'user-settings',
},
},
InvalidSettings: {
screen: InvalidSettingsScreen,
linking: {
path: '*',
},
},
},
});

const HomeTabs = createBottomTabNavigator({
screens: {
Feed: {
screen: FeedScreen,
},
Profile: {
screen: HomeScreen,
linking: {
path: 'users/:id',
},
},
Settings: {
screen: SettingsStack,
},
},
});

const RootStack = createStackNavigator({
screens: {
Home: {
screen: HomeTabs,
},
NotFound: {
screen: NotFoundScreen,
linking: {
path: '*',
},
},
},
});
结果导航状态

使用此配置,路径 /settings/notification 将解析为以下状态对象

const state = {
routes: [
{
name: 'Home',
state: {
index: 1,
routes: [
{ name: 'Feed' },
{
name: 'Settings',
state: {
routes: [
{ name: 'InvalidSettings', path: '/settings/notification' },
],
},
},
],
},
},
],
};

传递给 NotFound 屏幕的 route 将包含一个 path 属性,该属性对应于打开页面的路径。如果需要,您可以使用此属性来自定义此屏幕中显示的内容,例如在 WebView 中加载页面

function NotFoundScreen({ route }) {
if (route.path) {
return <WebView source={{ uri: `https://mywebsite.com/${route.path}` }} />;
}

return <Text>This screen doesn't exist!</Text>;
}

在进行服务器端渲染时,您还需要为 404 错误返回正确的状态代码。有关如何处理它的指南,请参阅 服务器端渲染文档

渲染初始路由

有时您希望确保某个屏幕始终作为导航器状态中的第一个屏幕存在。您可以使用 initialRouteName 属性来指定用于初始屏幕的屏幕。

在上面的示例中,如果您希望 Feed 屏幕成为 Home 下导航器中的初始路由,则您的配置将如下所示

const HomeTabs = createBottomTabNavigator({
screens: {
Feed: {
screen: FeedScreen,
},
Profile: {
screen: HomeScreen,
linking: {
path: 'users/:id',
},
},
Settings: {
screen: SettingsScreen,
linking: {
path: 'settings',
},
},
},
});

const RootStack = createStackNavigator({
screens: {
Home: {
screen: HomeTabs,
linking: {
initialRouteName: 'Feed',
},
},
NotFound: {
screen: NotFoundScreen,
linking: {
path: '*',
},
},
},
});
结果导航状态

使用此配置,路径 /users/42 将解析为以下状态对象

const state = {
routes: [
{
name: 'Home',
state: {
index: 1,
routes: [
{ name: 'Feed' },
{
name: 'Profile',
params: { id: '42' },
},
],
},
},
],
};
警告

initialRouteName 只会将屏幕添加到 React Navigation 的状态中。如果您的应用在 Web 上运行,则浏览器的历史记录将不包含此屏幕,因为用户从未访问过它。因此,如果用户按下浏览器的后退按钮,它将不会返回到此屏幕。

另一件需要记住的事情是,无法通过 URL 将参数传递给初始屏幕。因此,请确保您的初始路由不需要任何参数,或者在屏幕配置中指定 initialParams 以传递所需的参数。

在这种情况下,URL 中的任何参数都仅传递给与路径模式 users/:id 匹配的 Profile 屏幕,而 Feed 屏幕不接收任何参数。如果您希望在 Feed 屏幕中具有相同的参数,您可以指定一个自定义的 getStateFromPath 函数并复制这些参数。

同样,如果您想从子屏幕访问父屏幕的参数,您可以使用 React Context 来公开它们。

匹配确切路径

默认情况下,为每个屏幕定义的路径都相对于其父屏幕的路径进行匹配。考虑以下配置

const ProfileTabs = createBottomTabNavigator({
screens: {
Profile: {
screen: HomeScreen,
linking: {
path: 'users/:id',
},
},
},
});

const RootStack = createStackNavigator({
screens: {
Home: {
screen: ProfileTabs,
linking: {
path: 'feed',
},
},
},
});

在这里,您为 Home 屏幕以及子屏幕 Profile 屏幕定义了 path 属性。profile 屏幕指定路径 users/:id,但由于它嵌套在路径为 feed 的屏幕中,因此它将尝试匹配模式 feed/users/:id

这将导致 URL /feed 导航到 Home 屏幕,而 /feed/users/cal 导航到 Profile 屏幕。

在这种情况下,使用诸如 /users/cal 而不是 /feed/users/cal 之类的 URL 导航到 Profile 屏幕更有意义。要实现这一点,您可以覆盖相对匹配行为以进行 exact 匹配

const ProfileTabs = createBottomTabNavigator({
screens: {
Profile: {
screen: HomeScreen,
linking: {
path: 'users/:id',
exact: true,
},
},
},
});

const RootStack = createStackNavigator({
screens: {
Home: {
screen: ProfileTabs,
linking: {
path: 'feed',
},
},
},
});

exact 属性设置为 true 后,Profile 将忽略父屏幕的 path 配置,您将能够使用诸如 users/cal 之类的 URL 导航到 Profile

从路径中省略屏幕

有时,您可能不希望在路径中包含屏幕的路由名称。例如,假设您有一个 Home 屏幕和以下配置。当页面在浏览器中打开时,您将获得 /home 作为 URL

const RootStack = createStackNavigator({
screens: {
Home: {
screen: ProfileScreen,
linking: {
path: 'home',
},
},
Profile: {
screen: HomeScreen,
linking: {
path: 'users/:id',
},
},
},
});

但是,如果访问主屏幕时 URL 仅为 /,则会更好。

您可以将空字符串指定为路径,或者根本不指定路径,React Navigation 将不会将屏幕添加到路径中(可以将其视为向路径添加空字符串,这不会改变任何内容)

const RootStack = createStackNavigator({
screens: {
Home: {
screen: ProfileScreen,
linking: {
path: '',
},
},
Profile: {
screen: HomeScreen,
linking: {
path: 'users/:id',
},
},
},
});

序列化和解析参数

由于 URL 是字符串,因此当构造路径时,路由的任何参数也会转换为字符串。

例如,假设您有 URL /chat/1589842744264,配置如下

const RootStack = createStackNavigator({
screens: {
Chat: {
screen: ChatScreen,
linking: {
path: 'chat/:date',
},
},
},
});

当处理 URL 时,您的参数将如下所示

{ date: '1589842744264' }

在这里,date 参数被解析为字符串,因为 React Navigation 不知道它应该是时间戳,因此是数字。您可以通过提供自定义函数来解析来对其进行自定义

const RootStack = createStackNavigator({
screens: {
Chat: {
screen: ChatScreen,
linking: {
path: 'chat/:date',
parse: {
date: Number,
},
},
},
},
});

您还可以提供自己的函数来序列化参数。例如,假设您希望在路径中使用 DD-MM-YYYY 格式而不是时间戳

const RootStack = createStackNavigator({
screens: {
Chat: {
screen: ChatScreen,
linking: {
path: 'chat/:date',
parse: {
date: (date) => new Date(date).getTime(),
},
stringify: {
date: (date) => {
const d = new Date(date);

return d.getFullYear() + '-' + d.getMonth() + '-' + d.getDate();
},
},
},
},
},
});

根据您的要求,您可以使用此功能来解析和字符串化更复杂的数据。

匹配正则表达式

如果您需要更复杂的匹配逻辑,则可以使用正则表达式来匹配路径。例如,如果您想使用模式 @username 来匹配用户的个人资料,则可以使用正则表达式来匹配路径。这允许您为多个屏幕使用相同的路径模式,但微调匹配逻辑以使其更特定于特定屏幕。

正则表达式可以在参数名称后的括号 () 之间指定。例如

const RootStack = createStackNavigator({
screens: {
Feed: {
screen: FeedScreen,
linking: {
path: ':sort(latest|popular)',
},
},
Profile: {
screen: ProfileScreen,
linking: {
path: ':username(@[A-Za-z0-9_]+)',
},
},
},
});

只有当路径以 @ 开头,后跟字母数字字符或下划线时,才会匹配此路径。例如,URL /@jane 将匹配 Profile 屏幕,但 /jane 不会。

正则表达式旨在仅匹配路径段,而不是整个路径。因此,请避免在正则表达式中使用 /^$ 等。

警告

正则表达式是一项高级功能。它们无法进行验证以警告您潜在的问题,因此由您自己确保正则表达式正确。

路径别名

如果您希望同一屏幕具有多个路径,则可以使用 alias 属性来指定路径数组。这对于在过渡到新的 URL 结构时保持与旧 URL 的向后兼容性非常有用。

例如,如果您想同时匹配 /users/:id/:idProfile 屏幕,您可以这样做

const RootStack = createStackNavigator({
screens: {
Profile: {
screen: ProfileScreen,
linking: {
path: ':id',
alias: ['users/:id'],
},
},
},
});

在这种情况下,当 URL 为 /users/jane/jane 时,它将匹配 Profile 屏幕。path 是将用于生成 URL 的主要模式,例如,当在 Web 上的应用中导航到 Profile 屏幕时。生成 URL 时,将忽略 alias 中的模式。alias 模式不用于匹配嵌套导航器中的任何子屏幕。

在 Web 上,如果包含别名的屏幕包含嵌套导航器,则匹配别名的 URL 将仅用于匹配屏幕,并在应用呈现后更新为聚焦子屏幕的 URL。

alias 数组中的每个项目都可以是与 path 属性语法匹配的字符串,也可以是具有以下属性的对象

  • path (必需) - 要匹配的路径模式。
  • exact - 是否完全匹配路径。默认为 false。有关更多详细信息,请参阅 匹配确切路径
  • parse - 将路径段解析为参数值的函数。有关更多详细信息,请参阅 传递参数

高级用例

对于某些高级用例,指定映射可能不足以满足需求。为了处理这种情况,您可以指定一个自定义函数来将 URL 解析为状态对象 (getStateFromPath),以及一个自定义函数来将状态对象序列化为 URL (getPathFromState)。

例子

const linking = {
prefixes: ['https://mychat.com', 'mychat://'],
getStateFromPath: (path, options) => {
// Return a state object here
// You can also reuse the default logic by importing `getStateFromPath` from `@react-navigation/native`
},
getPathFromState(state, config) {
// Return a path string here
// You can also reuse the default logic by importing `getPathFromState` from `@react-navigation/native`
},

// ...
};

Playground

Playground 不适用于静态配置。