Skip to main content
Version: 6.x

配置链接

在本指南中,我们将配置 React Navigation 来处理外部链接。如果你想要:

¥In this guide, we will configure React Navigation to handle external links. This is necessary if you want to:

  1. 处理 Android 和 iOS 上的 React Native 应用中的深层链接

    ¥Handle deep links in React Native apps on Android and iOS

  2. 在网络上使用时启用浏览器中的 URL 集成

    ¥Enable URL integration in browser when using on web

  3. 使用 <Link />useLinkTo 使用路径进行导航。

    ¥Use <Link /> or useLinkTo to navigate using paths.

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

¥Make sure that you have configured deep links in your app before proceeding. If you have an Android or iOS app, remember to specify the prefixes option.

NavigationContainer 接受 linking 属性,可以更轻松地处理传入链接。你可以在 linking 属性中指定的两个最重要的属性是 prefixesconfig

¥The NavigationContainer accepts a linking prop that makes it easier to handle incoming links. The 2 of the most important properties you can specify in the linking prop are prefixes and config:

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

const linking = {
prefixes: [
/* your linking prefixes */
],
config: {
/* configuration for matching screens with paths */
},
};

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

当你指定 linking 属性时,React Navigation 将自动处理传入链接。在 Android 和 iOS 上,它将使用 React Native 的 Linking 模块 来处理传入链接,无论是在使用链接打开应用时,还是在打开应用时收到新链接时。在 Web 上,它将使用 历史 API 将 URL 与浏览器同步。

¥When you specify the linking prop, React Navigation will handle incoming links automatically. On Android and iOS, it'll use React Native's Linking module to handle incoming links, both when the app was opened with the link, and when new links are received when the app is open. On the Web, it'll use the History API to sync the URL with the browser.

警告

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

¥Currently there seems to be bug (facebook/react-native#25675) which results in it never resolving on Android. We add a timeout to avoid getting stuck forever, but it means that the link might not be handled in some cases.

你还可以将 fallback 属性传递给 NavigationContainer,它控制 React Navigation 尝试解析初始深层链接 URL 时显示的内容。

¥You can also pass a fallback prop to NavigationContainer which controls what's displayed when React Navigation is trying to resolve the initial deep link URL.

前缀

¥Prefixes

如果你配置了 通用链接安卓应用链接,则 prefixes 选项可用于指定自定义方案(例如 mychat://)以及主机名和域名(例如 https://mychat.com)。

¥The prefixes option can be used to specify custom schemes (e.g. mychat://) as well as host & domain names (e.g. https://mychat.com) if you have configured Universal Links or Android App Links.

例如:

¥For example:

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

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

¥Note that the prefixes option is not supported on Web. The host & domain names will be automatically determined from the Website URL in the browser. If your app runs only on Web, then you can omit this option from the config.

多个子域​

¥Multiple subdomains​

要匹配关联域的所有子域,你可以通过前缀 * 来指定通配符。在特定域的开始之前。请注意,由于星号后面有句点,因此 *.mychat.com 的条目与 mychat.com 不匹配。要启用 *.mychat.commychat.com 的匹配,你需要为每个提供单独的前缀条目。

¥To match all subdomains of an associated domain, you can specify a wildcard by prefixing *. before the beginning of a specific domain. Note that an entry for *.mychat.com does not match mychat.com because of the period after the asterisk. To enable matching for both *.mychat.com and mychat.com, you need to provide a separate prefix entry for each.

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

过滤某些路径

¥Filtering certain paths

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

¥Sometimes we may not want to handle all incoming links. For example, we may want to filter out links meant for authentication (e.g. expo-auth-session) or other purposes instead of navigating to a specific screen.

为此,你可以使用 filter 选项:

¥To achieve this, you can use the filter option:

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

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

¥This is not supported on Web as we always need to handle the URL of the page.

将路径映射到路由名称

¥Mapping path to route names

要处理链接,你需要将其转换为有效的 导航状态,反之亦然。例如,路径 /rooms/chat?user=jane 可以转换为如下状态对象:

¥To handle a link, you need to translate it to a valid navigation state and vice versa. For example, the path /rooms/chat?user=jane may be translated to a state object like this:

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

默认情况下,React Navigation 在解析 URL 时将使用路径段作为路由名称。但直接将路径段转换为路由名称可能不是预期的行为。

¥By default, React Navigation will use the path segments as the route name when parsing the URL. But directly translating path segments to route names may not be the expected behavior.

例如,你可能希望将路径 /feed/latest 解析为:

¥For example, you might want to parse the path /feed/latest to something like:

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

你可以在 linking 中指定 config 选项来控制如何解析深层链接以满足你的需求。

¥You can specify the config option in linking to control how the deep link is parsed to suit your needs.

const config = {
screens: {
Chat: 'feed/:sort',
Profile: 'user',
},
};

这里 Chat 是处理 URL /feed 的屏幕的名称,Profile 处理 URL /user

¥Here Chat is the name of the screen that handles the URL /feed, and Profile handles the URL /user.

然后可以将配置选项通过 linking 属性传递给容器:

¥The config option can then be passed in the linking prop to the container:

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

const config = {
screens: {
Chat: 'feed/:sort',
Profile: 'user',
},
};

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

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

配置对象必须与你的应用的导航结构匹配。例如,上面的配置是如果根导航器中有 ChatProfile 屏幕:

¥The config object must match the navigation structure for your app. For example, the above configuration is if you have Chat and Profile screens in the navigator at the root:

function App() {
return (
<Stack.Navigator>
<Stack.Screen name="Chat" component={ChatScreen} />
<Stack.Screen name="Profile" component={ProfileScreen} />
</Stack.Navigator>
);
}

如果你的 Chat 屏幕位于嵌套导航器内,我们需要考虑到这一点。例如,考虑以下结构,其中 Profile 屏幕位于根目录,但 Chat 屏幕嵌套在 Home 内:

¥If your Chat screen is inside a nested navigator, we'd need to account for that. For example, consider the following structure where your Profile screen is at the root, but the Chat screen is nested inside Home:

function App() {
return (
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Profile" component={ProfileScreen} />
</Stack.Navigator>
);
}

function HomeScreen() {
return (
<Tab.Navigator>
<Tab.Screen name="Chat" component={ChatScreen} />
</Tab.Navigator>
);
}

对于上面的结构,我们的配置将如下所示:

¥For above structure, our configuration will look like this:

const config = {
screens: {
Home: {
screens: {
Chat: 'feed/:sort',
},
},
Profile: 'user',
},
};

同样,任何嵌套都需要在配置中反映出来。详细信息请参见 处理嵌套导航器

¥Similarly, any nesting needs to be reflected in the configuration. See handling nested navigators for more details.

传递参数

¥Passing params

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

¥A common use case is to pass params to a screen to pass some data. For example, you may want the Profile screen to have an id param to know which user's profile it is. It's possible to pass params to a screen through a URL when handling deep links.

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

¥By default, query params are parsed to get the params for a screen. For example, with the above example, the URL /user?id=wojciech will pass the id param to the Profile screen.

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

¥You can also customize how the params are parsed from the URL. Let's say you want the URL to look like /user/wojciech where the id param is wojciech instead of having the id in query params. You can do this by specifying user/:id for the path. When the path segment starts with :, it'll be treated as a param. For example, the URL /user/wojciech would resolve to Profile screen with the string wojciech as a value of the id param and will be available in route.params.id in Profile screen.

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

¥By default, all params are treated as strings. You can also customize how to parse them by specifying a function in the parse property to parse the param, and a function in the stringify property to convert it back to a string.

如果你想解析 /user/wojciech/settings 以生成参数 { id: 'user-wojciech' section: 'settings' },你可以使 Profile 的配置如下所示:

¥If you wanted to resolve /user/wojciech/settings to result in the params { id: 'user-wojciech' section: 'settings' }, you could make Profile's config to look like this:

const config = {
screens: {
Profile: {
path: 'user/:id/:section',
parse: {
id: (id) => `user-${id}`,
},
stringify: {
id: (id) => id.replace(/^user-/, ''),
},
},
},
};

这将导致类似的结果:

¥This will result in something like:

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

将参数标记为可选

¥Marking params as optional

有时,根据某些条件,参数可能会或可能不会出现在 URL 中。例如,在上述场景中,你可能并不总是在 URL 中包含部分参数,即 /user/wojciech/settings/user/wojciech 都应转到 Profile 屏幕,但 section 参数(在本例中值为 settings)可能会或可能不会展示。

¥Sometimes a param may or may not be present in the URL depending on certain conditions. For example, in the above scenario, you may not always have the section parameter in the URL, i.e. both /user/wojciech/settings and /user/wojciech should go to the Profile screen, but the section param (with the value settings in this case) may or may not be present.

在这种情况下,你需要将 section 参数标记为可选。你可以通过在参数名称后添加 ? 后缀来实现:

¥In this case, you would need to mark the section param as optional. You can do it by adding the ? suffix after the param name:

const config = {
screens: {
Profile: {
path: 'user/:id/:section?',
parse: {
id: (id) => `user-${id}`,
},
stringify: {
id: (id) => id.replace(/^user-/, ''),
},
},
},
};

使用 URL /users/wojciech,这将导致:

¥With the URL /users/wojciech, this will result in:

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

如果 URL 包含 section 参数,例如 /users/wojciech/settings,这将导致具有相同配置的以下结果:

¥If the URL contains a section param, e.g. /users/wojciech/settings, this will result in the following with the same config:

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

处理嵌套导航器

¥Handling nested navigators

有时,你会将目标导航器嵌套在不属于深层链接的其他导航器中。例如,假设你的导航结构如下所示:

¥Sometimes you'll have the target navigator nested in other navigators which aren't part of the deep link. For example, let's say your navigation structure looks like this:

function Home() {
return (
<Tab.Navigator>
<Tab.Screen name="Profile" component={Profile} />
<Tab.Screen name="Feed" component={Feed} />
</Tab.Navigator>
);
}

function App() {
return (
<Stack.Navigator>
<Stack.Screen name="Home" component={Home} />
<Stack.Screen name="Settings" component={Settings} />
</Stack.Navigator>
);
}

这里,你在根目录中有一个堆栈导航器,在根堆栈的 Home 屏幕内,你有一个带有各种屏幕的选项卡导航器。通过此结构,假设你希望路径 /users/:id 转到 Profile 屏幕。你可以像这样表达嵌套配置:

¥Here you have a stack navigator in the root, and inside the Home screen of the root stack, you have a tab navigator with various screens. With this structure, let's say you want the path /users/:id to go to the Profile screen. You can express the nested config like so:

const config = {
screens: {
Home: {
screens: {
Profile: 'users/:id',
},
},
},
};

在此配置中,你指定应将 Profile 屏幕解析为 users/:id 模式,并将其嵌套在 Home 屏幕内。然后解析 users/jane 将得到以下状态对象:

¥In this config, you specify that the Profile screen should be resolved for the users/:id pattern and it's nested inside the Home screen. Then parsing users/jane will result in the following state object:

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

需要注意的是,状态对象必须与嵌套导航器的层次结构相匹配。否则状态将被丢弃。

¥It's important to note that the state object must match the hierarchy of nested navigators. Otherwise the state will be discarded.

处理不匹配的路由或 404

¥Handling unmatched routes or 404

如果你的应用使用无效 URL 打开,大多数时候你希望显示包含一些信息的错误页面。在网络上,这通常称为 404 - 或页面未找到错误。

¥If your app is opened with an invalid URL, most of the times you'd want to show an error page with some information. On the web, this is commonly known as 404 - or page not found error.

为了解决这个问题,你需要定义一个包罗万象的路由,如果没有其他路由与该路径匹配,则将呈现该路由。你可以通过为路径匹配模式指定 * 来完成此操作。

¥To handle this, you'll need to define a catch-all route that will be rendered if no other routes match the path. You can do it by specifying * for the path matching pattern.

例如:

¥For example:

const config = {
screens: {
Home: {
initialRouteName: 'Feed',
screens: {
Profile: 'users/:id',
Settings: 'settings',
},
},
NotFound: '*',
},
};

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

¥Here, we have defined a route named NotFound and set it to match * aka everything. If the path didn't match user/:id or settings, it'll be matched by this route.

因此,像 /library/settings/notification 这样的路径将解析为以下状态对象:

¥So, a path like /library or /settings/notification will resolve to the following state object:

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

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

¥You can even go more specific, for example, say if you want to show a different screen for invalid paths under /settings, you can specify such a pattern under Settings:

const config = {
screens: {
Home: {
initialRouteName: 'Feed',
screens: {
Profile: 'users/:id',
Settings: {
path: 'settings',
screens: {
InvalidSettings: '*',
},
},
},
},
NotFound: '*',
},
};

通过此配置,路径 /settings/notification 将解析为以下状态对象:

¥With this configuration, the path /settings/notification will resolve to the following state object:

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

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

¥The route passed to the NotFound screen will contain a path property which corresponds to the path that opened the page. If you need, you can use this property to customize what's shown in this screen, e.g. load the page in a 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 错误的正确状态代码。有关如何处理它的指南,请参阅 服务器渲染文档

¥When doing server rendering, you'd also want to return correct status code for 404 errors. See server rendering docs for a guide on how to handle it.

渲染初始路由

¥Rendering an initial route

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

¥Sometimes you want to ensure that a certain screen will always be present as the first screen in the navigator's state. You can use the initialRouteName property to specify the screen to use for the initial screen.

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

¥In the above example, if you want the Feed screen to be the initial route in the navigator under Home, your config will look like this:

const config = {
screens: {
Home: {
initialRouteName: 'Feed',
screens: {
Profile: 'users/:id',
Settings: 'settings',
},
},
},
};

然后,路径 /users/42 将解析为以下状态对象:

¥Then, the path /users/42 will resolve to the following state object:

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

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

¥The initialRouteName will add the screen to React Navigation's state only. If your app is running on the Web, the browser's history will not contain this screen as the user has never visited it. So, if the user presses the browser's back button, it'll not go back to this screen.

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

¥Another thing to keep in mind is that it's not possible to pass params to the initial screen through the URL. So make sure that your initial route doesn't need any params or specify initialParams in the screen configuration to pass the required params.

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

¥In this case, any params in the URL are only passed to the Profile screen which matches the path pattern users/:id, and the Feed screen doesn't receive any params. If you want to have the same params in the Feed screen, you can specify a custom getStateFromPath function and copy those params.

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

¥Similarly, if you want to access params of a parent screen from a child screen, you can use React Context to expose them.

匹配精确路径

¥Matching exact paths

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

¥By default, paths defined for each screen are matched against the URL relative to their parent screen's path. Consider the following config:

const config = {
screens: {
Home: {
path: 'feed',
screens: {
Profile: 'users/:id',
},
},
},
};

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

¥Here, you have a path property defined for the Home screen, as well as the child Profile screen. The profile screen specifies the path users/:id, but since it's nested inside a screen with the path feed, it'll try to match the pattern feed/users/:id.

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

¥This will result in the URL /feed navigating to Home screen, and /feed/users/cal navigating to the Profile screen.

在这种情况下,使用 /users/cal 这样的 URL 导航到 Profile 屏幕比使用 /feed/users/cal 更有意义。为此,你可以将相对匹配行为覆盖为 exact 匹配:

¥In this case, it makes more sense to navigate to the Profile screen using a URL like /users/cal, rather than /feed/users/cal. To achieve this, you can override the relative matching behavior to exact matching:

const config = {
screens: {
Home: {
path: 'feed',
screens: {
Profile: {
path: 'users/:id',
exact: true,
},
},
},
},
};

exact 属性设置为 true 时,Profile 将忽略父屏幕的 path 配置,你将能够使用 users/cal 等 URL 导航到 Profile

¥With exact property set to true, Profile will ignore the parent screen's path config and you'll be able to navigate to Profile using a URL like users/cal.

从路径中省略屏幕

¥Omitting a screen from path

有时,你可能不希望路径中包含屏幕的路由名称。例如,假设你有一个 Home 屏幕,而我们的 导航状态 看起来像这样:

¥Sometimes, you may not want to have the route name of a screen in the path. For example, let's say you have a Home screen and our navigation state looks like this:

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

当此状态序列化到具有以下配置的路径时,你将获得 /home

¥When this state is serialized to a path with the following config, you'll get /home:

const config = {
screens: {
Home: {
path: 'home',
screens: {
Profile: 'users/:id',
},
},
},
};

但如果访问主屏幕时 URL 是 / 就更好了。你可以指定一个空字符串作为路径,或者根本不指定路径,React Navigation 不会将屏幕添加到路径中(就像将空字符串添加到路径中一样,这不会改变任何内容):

¥But it'll be nicer if the URL was just / when visiting the home screen. You can specify an empty string as path or not specify a path at all, and React Navigation won't add the screen to the path (think of it like adding empty string to the path, which doesn't change anything):

const config = {
screens: {
Home: {
path: '',
screens: {
Profile: 'users/:id',
},
},
},
};

序列化和解析参数

¥Serializing and parsing params

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

¥Since URLs are strings, any params you have for routes are also converted to strings when constructing the path.

例如,假设你有如下状态:

¥For example, say you have a state like following:

const state = {
routes: [
{
name: 'Chat',
params: { at: 1589842744264 },
},
];
}

它将使用以下配置转换为 chat/1589842744264

¥It'll be converted to chat/1589842744264 with the following config:

const config = {
screens: {
Chat: 'chat/:date',
},
};

解析此路径时,你将得到以下状态:

¥When parsing this path, you'll get the following state:

const state = {
routes: [
{
name: 'Chat',
params: { date: '1589842744264' },
},
];
}

在这里,date 参数被解析为字符串,因为 React Navigation 不知道它应该是时间戳,因此也不知道数字。你可以通过提供用于解析的自定义函数来自定义它:

¥Here, the date param was parsed as a string because React Navigation doesn't know that it's supposed to be a timestamp, and hence number. You can customize it by providing a custom function to use for parsing:

const config = {
screens: {
Chat: {
path: 'chat/:date',
parse: {
date: Number,
},
},
},
};

你还可以提供自定义函数来序列化参数。例如,假设你想在路径中使用 DD-MM-YYYY 格式而不是时间戳:

¥You can also provide a custom function to serialize the params. For example, let's say that you want to use a DD-MM-YYYY format in the path instead of a timestamp:

const config = {
screens: {
Chat: {
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();
},
},
},
},
};

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

¥Depending on your requirements, you can use this functionality to parse and stringify more complex data.

高级案例

¥Advanced cases

对于某些高级情况,指定映射可能还不够。为了处理这种情况,你可以指定一个自定义函数将 URL 解析为状态对象 (getStateFromPath),并指定一个自定义函数将状态对象序列化为 URL (getPathFromState)。

¥For some advanced cases, specifying the mapping may not be sufficient. To handle such cases, you can specify a custom function to parse the URL into a state object (getStateFromPath), and a custom function to serialize the state object into an URL (getPathFromState).

示例:

¥Example:

const linking = {
prefixes: ['https://mychat.com', 'mychat://'],
config: {
screens: {
Chat: 'feed/:sort',
},
},
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`
},
};

更新配置

¥Updating config

旧版本的 React Navigation 的链接配置格式略有不同。旧配置允许在对象中使用简单的键值对,而不管导航器的嵌套如何:

¥Older versions of React Navigation had a slightly different configuration format for linking. The old config allowed a simple key value pair in the object regardless of nesting of navigators:

const config = {
Home: 'home',
Feed: 'feed',
Profile: 'profile',
Settings: 'settings',
};

假设你的 FeedProfile 屏幕嵌套在 Home 内。即使你没有使用上面的配置进行这样的嵌套,只要 URL 是 /home/profile,它就可以工作。此外,它还会将路径段和路由名称视为相同,这意味着你可以深层链接到配置中未指定的屏幕。例如,如果 Home 中有一个 Albums 屏幕,则深层链接 /home/Albums 将导航到该屏幕。虽然这在某些情况下可能是可取的,但无法阻止访问特定屏幕。这种方法还使得不可能出现 404 屏幕之类的情况,因为任何路由名称都是有效路径。

¥Let's say, your Feed and Profile screens are nested inside Home. Even if you don't have such a nesting with the above configuration, as long as the URL was /home/profile, it would work. Furthermore, it would also treat path segments and route names the same, which means that you could deep link to a screen that's not specified in the configuration. For example, if you have a Albums screen inside Home, the deep link /home/Albums would navigate to that screen. While that may be desirable in some cases, there's no way to prevent access to specific screens. This approach also makes it impossible to have something like a 404 screen since any route name is a valid path.

最新版本的 React Navigation 使用不同的配置格式,在这方面更加严格:

¥Latest versions of React Navigation use a different config format which is stricter in this regard:

  • 配置的形状必须与导航结构中嵌套的形状匹配

    ¥The shape of the config must match the shape of the nesting in the navigation structure

  • 只有配置中定义的屏幕才有资格进行深度链接

    ¥Only screens defined in the config will be eligible for deep linking

因此,你可以将上述配置重构为以下格式:

¥So, you'd refactor the above config to the following format:

const config = {
screens: {
Home: {
path: 'home',
screens: {
Feed: 'feed',
Profile: 'profile',
},
},
Settings: 'settings',
},
};

这里,配置对象有一个新的 screens 属性,FeedProfile 配置现在嵌套在 Home 下以匹配导航结构。

¥Here, there's a new screens property to the configuration object, and the Feed and Profile configs are now nested under Home to match the navigation structure.

如果你使用旧格式,它将继续工作,无需任何更改。但是,你将无法指定通配符模式来处理不匹配的屏幕或防止屏幕深层链接。旧格式将在下一个主要版本中删除。因此,我们建议你尽可能迁移到新格式。

¥If you have the old format, it will continue to work without any changes. However, you won't be able to specify a wildcard pattern to handle unmatched screens or prevent screens from being deep linked. The old format will be removed in the next major release. So we recommend to migrate to the new format when you can.

在线运行

¥Playground

你可以尝试自定义下面的配置和路径,并查看如何解析路径。

¥You can play around with customizing the config and path below, and see how the path is parsed.

Home
Feed
Profile
id:"vergil"
Settings

示例应用

¥Example App

在示例应用中,你将使用 Expo 托管工作流程。本指南将重点关注创建深度链接配置,而不是创建组件本身,但你始终可以检查 github 仓库 中的完整实现。

¥In the example app, you will use the Expo managed workflow. The guide will focus on creating the deep linking configuration and not on creating the components themselves, but you can always check the full implementation in the github repo.

首先,你需要决定应用的导航结构。为了简单起见,主导航器将是带有两个屏幕的底部选项卡导航器。它的第一个屏幕将是一个简单的堆栈导航器,称为 HomeStack,有两个屏幕:HomeProfile,第二个选项卡屏幕将只是一个简单的屏幕,没有任何嵌套导航器,称为 Settings

¥First, you need to decide the navigation structure of your app. To keep it simple, the main navigator will be bottom-tabs navigator with two screens. Its first screen will be a simple stack navigator, called HomeStack, with two screens: Home and Profile, and the second tabs screen will be just a simple one without any nested navigators, called Settings:

BottomTabs
├── Stack (HomeStack)
│   ├── Home
│   └── Profile
└── Settings

创建导航结构后,你可以创建深度链接的配置,其中包含每个屏幕到路径段的映射。例如:

¥After creating the navigation structure, you can create a config for deep linking, which will contain mappings for each screen to a path segment. For example:

const config = {
screens: {
HomeStack: {
screens: {
Home: 'home',
Profile: 'user',
},
},
Settings: 'settings',
},
};

如你所见,HomeProfile 嵌套在 HomeStackscreens 属性中。这意味着当你传递 /home URL 时,它将被解析为 HomeStack->Home 状态对象(类似地,对于 /user,它将是 HomeStack->Profile)。该对象中的嵌套应与导航器的嵌套相匹配。

¥As you can see, Home and Profile are nested in the screens property of HomeStack. This means that when you pass the /home URL, it will be resolved to a HomeStack->Home state object (similarly for /user it would be HomeStack->Profile). The nesting in this object should match the nesting of our navigators.

这里,HomeStack 属性包含一个配置对象。配置可以根据需要深入,例如如果 Home 是一个导航器,你可以将其设为具有 screens 属性的对象,并在其中放入更多屏幕或导航器,从而使 URL 字符串更具可读性。

¥Here, the HomeStack property contains a config object. The config can go as deep as you want, e.g. if Home was a navigator, you could make it an object with screens property, and put more screens or navigators inside it, making the URL string much more readable.

如果你希望将特定屏幕用作导航器中的初始屏幕怎么办?例如,如果你有一个可以打开 Home 屏幕的 URL,你希望能够使用导航的 navigation.goBack() 方法从该屏幕导航到 Profile。可以通过为导航器定义 initialRouteName 来实现。它看起来像这样:

¥What if you wanted a specific screen to used as the initial screen in the navigator? For example, if you had a URL that would open Home screen, you would like to be able to navigate to Profile from it by using navigation's navigation.goBack() method. It is possible by defining initialRouteName for a navigator. It would look like this:

const config = {
screens: {
HomeStack: {
initialRouteName: 'Profile',
screens: {
Home: 'home',
Profile: 'user',
},
},
Settings: 'settings',
},
};