Конфигурирование ссылок¶
В этом руководстве мы настроим React Navigation для работы с внешними ссылками. Это необходимо, если вы хотите:
- Обрабатывать глубокие ссылки в приложениях React Native на Android и iOS
- Включить интеграцию URL в браузере при использовании в Интернете
- Используйте
<Link />
илиuseLinkTo
для навигации по путям.
Прежде чем приступить к работе, убедитесь, что в вашем приложении настроены deep links. Если у вас приложение для Android или iOS, не забудьте указать параметр prefixes
.
Контейнер NavigationContainer
принимает свойство linking
, которое облегчает работу с входящими ссылками. Два наиболее важных свойства, которые можно указать в реквизите linking
, - это prefixes
и config
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
При указании свойства linking
React Navigation будет автоматически обрабатывать входящие ссылки. На Android и iOS для обработки входящих ссылок используется модуль React Native Linking
, как при открытии приложения со ссылкой, так и при получении новых ссылок при открытом приложении. В Интернете для синхронизации URL с браузером используется History API.
Примечание
В настоящее время, похоже, существует ошибка (facebook/react-native#25675), из-за которой ссылка не разрешается на Android. Мы добавляем таймаут, чтобы избежать вечного застревания, но это означает, что в некоторых случаях ссылка может быть не обработана.
Также в NavigationContainer
можно передать параметр fallback
, который определяет, что будет отображаться, когда React Navigation пытается разрешить начальный URL глубокой ссылки.
Префиксы¶
Опция prefixes
может быть использована для указания пользовательских схем (например, mychat://
), а также имен хостов и доменов (например, https://mychat.com
), если вы настроили Universal Links или Android App Links.
Например:
1 2 3 |
|
Обратите внимание, что опция prefixes
не поддерживается в Web. Имена хоста и домена будут автоматически определяться из URL сайта в браузере. Если ваше приложение работает только на Web, то эту опцию можно опустить в конфигурации.
Несколько поддоменов¶
Чтобы найти все поддомены связанного домена, можно указать подстановочный знак, добавив префикс *
. перед началом определенного домена. Обратите внимание, что запись для *.mychat.com
не соответствует mychat.com
из-за точки после звездочки. Чтобы обеспечить соответствие для доменов *.mychat.com
и mychat.com
, необходимо создать отдельный префикс для каждого из них.
1 2 3 4 5 6 7 |
|
Сопоставление путей с именами маршрутов¶
Для работы со ссылкой необходимо перевести ее в допустимое состояние navigation state и наоборот. Например, путь /rooms/chat?user=jane
может быть преобразован в объект state следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
По умолчанию React Navigation использует сегменты пути в качестве имени маршрута при разборе URL. Однако прямое преобразование сегментов пути в имена маршрутов может не соответствовать ожидаемому поведению.
Например, для разбора пути /feed/latest
может потребоваться что-то вроде:
1 2 3 4 5 6 7 8 9 10 |
|
Вы можете указать опцию config
в linking
, чтобы управлять разбором глубокой ссылки в соответствии с вашими потребностями.
1 2 3 4 5 6 |
|
Здесь Chat
- это имя экрана, который обрабатывает URL /feed
, а Profile
- URL /user
.
Затем опция конфигурации может быть передана в свойстве linking
контейнеру:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
Объект config
должен соответствовать структуре навигации вашего приложения. Например, приведенная выше конфигурация будет работать, если в корневой части навигатора находятся экраны Chat
и Profile
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
Если экран Chat
находится внутри вложенного навигатора, то это необходимо учитывать. Например, рассмотрим следующую структуру, в которой экран Profile
находится в корне, а экран Chat
вложен внутрь Home
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
Для приведенной выше структуры наша конфигурация будет выглядеть следующим образом:
1 2 3 4 5 6 7 8 9 10 |
|
Аналогично, любая вложенность должна быть отражена в конфигурации. Подробнее об этом см. в разделе "обработка вложенных навигаторов".
Передача параметров¶
Частым случаем использования является передача параметров экрану для передачи некоторых данных. Например, вы можете захотеть, чтобы экран Profile
имел параметр id
, чтобы знать, профиль какого пользователя он использует. При работе с глубокими ссылками можно передавать параметры экрану через URL.
По умолчанию параметры запроса анализируются для получения параметров экрана. Например, в приведенном выше примере URL /user?id=wojciech
передаст параметр id
на экран Profile
.
Вы также можете настроить способ разбора параметров из URL. Допустим, вы хотите, чтобы URL выглядел как /user/wojciech
, где параметр id
- это wojciech
, а не id
в параметрах запроса. Это можно сделать, указав user/:id
для пути
. Если сегмент пути начинается с :
, то он будет рассматриваться как параметр. Например, URL /user/wojciech
разрешится в экран Profile
со строкой wojciech
в качестве значения параметра id
и будет доступен в route.params.id
в экране Profile
.
По умолчанию все параметры обрабатываются как строки. Вы также можете настроить их разбор, указав в свойстве parse
функцию для разбора параметра, а в свойстве stringify
- функцию для его обратного преобразования в строку.
Если вы хотите, чтобы разрешение /user/wojciech/settings
приводило к параметрам { id: 'user-wojciech' section: 'settings' }
, вы можете сделать конфигурацию Profile
такой:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
В результате получится что-то вроде:
1 2 3 4 5 6 7 8 9 10 11 |
|
Пометка параметров как необязательных¶
Иногда параметр может присутствовать или не присутствовать в URL в зависимости от определенных условий. Например, в приведенном выше сценарии параметр section может присутствовать в URL не всегда, т.е. и /user/wojciech/settings
, и /user/wojciech
должны переходить на экран Profile
, но параметр section
(в данном случае со значением settings
) может присутствовать, а может и нет.
В этом случае необходимо пометить параметр section
как необязательный. Это можно сделать, добавив суффикс ?
после имени параметра:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
Для URL /users/wojciech
это приведет к следующему результату:
1 2 3 4 5 6 7 8 |
|
Если URL содержит параметр section
, например /users/wojciech/settings
, то с той же конфигурацией это приведет к следующему результату:
1 2 3 4 5 6 7 8 9 10 11 |
|
Работа с вложенными навигаторами¶
Иногда целевой навигатор оказывается вложенным в другие навигаторы, не являющиеся частью глубокой ссылки. Допустим, структура навигации выглядит следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
Здесь в корне имеется навигатор стеков, а внутри экрана Home
корневого стека - навигатор вкладок с различными экранами. При такой структуре, допустим, вы хотите, чтобы путь /users/:id
вел на экран Profile
. Вы можете выразить вложенную конфигурацию следующим образом:
1 2 3 4 5 6 7 8 9 |
|
В этой конфигурации указывается, что экран Profile
должен быть разрешен по шаблону users/:id
и вложен внутрь экрана Home
. Тогда при разборе экрана users/jane
будет получен следующий объект состояния:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
Важно отметить, что объект state должен соответствовать иерархии вложенных навигаторов. В противном случае состояние будет отброшено.
Обработка несоответствующих маршрутов или 404¶
Если ваше приложение открывается по некорректному URL-адресу, то в большинстве случаев вы хотите показать страницу ошибки с некоторой информацией. В Интернете это обычно называется 404 - ошибка "страница не найдена".
Чтобы справиться с этой проблемой, необходимо определить универсальный маршрут, который будет отображаться, если ни один другой маршрут не соответствует пути. Это можно сделать, указав *
для шаблона соответствия пути.
Например:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Здесь мы определили маршрут с именем NotFound
и установили, что он будет соответствовать *
aka все. Если путь не соответствует user/:id
или Settings
, то он будет соответствовать этому маршруту.
Таким образом, путь типа /library
или /settings/notification
будет разрешаться в следующий объект состояния:
1 2 3 |
|
Можно даже уточнить, например, если вы хотите показывать другой экран для недействительных путей в /settings
, вы можете указать такой шаблон в Settings
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
При такой конфигурации путь /settings/notification
будет разрешаться в следующий объект состояния:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
Маршрут route
, передаваемый экрану NotFound
, будет содержать свойство path
, соответствующее пути, по которому была открыта страница. При необходимости это свойство можно использовать для настройки того, что будет показано на этом экране, например, для загрузки страницы в WebView
:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
При серверном рендеринге также необходимо возвращать корректный код состояния для ошибок 404. Как это сделать, смотрите в server rendering docs.
Рендеринг начального маршрута¶
Иногда требуется, чтобы определенный экран всегда присутствовал в качестве первого экрана в состоянии навигатора. Для этого можно использовать свойство initialRouteName
, чтобы указать экран, который будет использоваться в качестве начального.
В приведенном выше примере, если вы хотите, чтобы экран Feed
был начальным маршрутом в навигаторе в разделе Home
, ваша конфигурация будет выглядеть следующим образом:
1 2 3 4 5 6 7 8 9 10 11 |
|
Тогда путь /users/42
разрешится в следующий объект состояния:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
Передать параметры в начальный экран через URL невозможно. Поэтому убедитесь, что ваш начальный маршрут не нуждается в параметрах, или укажите initialParams
для передачи необходимых параметров.
В этом случае все параметры в URL передаются только экрану Profile
, который соответствует шаблону пути users/:id
, а экран Feed
не получает никаких параметров. Если вы хотите иметь те же параметры в экране Feed
, вы можете указать пользовательскую функцию getStateFromPath
и скопировать эти параметры.
Аналогично, если необходимо получить доступ к параметрам родительского экрана из дочернего экрана, можно использовать React Context для их отображения.
Соответствие точным путям¶
По умолчанию пути, определенные для каждого экрана, сопоставляются с URL относительно пути их родительского экрана. Рассмотрим следующую конфигурацию:
1 2 3 4 5 6 7 8 9 10 |
|
Здесь свойство path
определено для экрана Home
, а также для дочернего экрана Profile
. Экран профиля указывает путь users/:id
, но поскольку он вложен в экран с путем feed
, то он будет пытаться соответствовать шаблону feed/users/:id
.
В результате URL /feed
перейдет на экран Home
, а /feed/users/cal
- на экран Profile
.
В данном случае логичнее переходить к экрану Profile
, используя URL типа /users/cal
, а не /feed/users/cal
. Для этого можно переопределить поведение относительного соответствия на exact
соответствие:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
Если свойство exact
установлено в true
, то Profile
будет игнорировать конфигурацию path
родительского экрана, и вы сможете перейти к Profile
, используя URL типа users/cal
.
Исключение экрана из пути¶
Иногда бывает нежелательно указывать в пути маршрутное имя экрана. Например, допустим, у вас есть экран Home
, а наш [navigation state] (navigation-state.md) выглядит следующим образом:
1 2 3 |
|
Если это состояние сериализовать в путь со следующей конфигурацией, то получится /home
:
1 2 3 4 5 6 7 8 9 10 |
|
Но было бы приятнее, если бы при посещении главного экрана URL был просто /
. В качестве пути можно указать пустую строку или вообще не указывать путь, и React Navigation не будет добавлять экран в путь (считайте, что это как добавление пустой строки в путь, которое ничего не меняет):
1 2 3 4 5 6 7 8 9 10 |
|
Сериализация и разбор параметров¶
Поскольку URL являются строками, любые параметры, задаваемые для маршрутов, также преобразуются в строки при построении пути.
Например, у вас есть состояние, подобное следующему:
1 2 3 4 5 6 7 8 |
|
Он будет преобразован в chat/1589842744264
со следующей конфигурацией:
1 2 3 4 5 |
|
При разборе этого пути вы получите следующее состояние:
1 2 3 4 5 6 7 8 |
|
Здесь параметр date
был разобран как строка, поскольку React Navigation не знает, что это должна быть временная метка, а значит, число. Вы можете настроить его, предоставив пользовательскую функцию, которая будет использоваться для разбора:
1 2 3 4 5 6 7 8 9 10 |
|
Вы также можете предоставить пользовательскую функцию для сериализации параметров. Например, допустим, вы хотите использовать в пути формат DD-MM-YYYY вместо временной метки:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
В зависимости от ваших требований, вы можете использовать эту функциональность для анализа и структуризации более сложных данных.
Расширенные случаи¶
Для некоторых сложных случаев указания отображения может быть недостаточно. Для таких случаев можно указать пользовательскую функцию для разбора URL в объект состояния (getStateFromPath
) и пользовательскую функцию для сериализации объекта состояния в URL (getPathFromState
).
Пример:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Обновление конфигурации¶
Старые версии React Navigation имели несколько иной формат конфигурации для связывания. Старая конфигурация позволяла использовать простую пару ключ-значение в объекте независимо от вложенности навигаторов:
1 2 3 4 5 6 |
|
Допустим, экраны Feed
и Profile
вложены внутрь Home
. Даже если у вас нет такой вложенности с приведенной выше конфигурацией, до тех пор, пока URL будет /home/profile
, это будет работать. Более того, сегменты пути и имена маршрутов будут рассматриваться одинаково, что означает возможность глубокой ссылки на экран, не указанный в конфигурации. Например, если экран Albums
находится внутри Home
, то глубокая ссылка /home/Albums
будет переходить на этот экран. Хотя в некоторых случаях это и желательно, но запретить доступ к определенным экранам невозможно. Такой подход также делает невозможным создание чего-то вроде экрана 404, поскольку любое имя маршрута является допустимым путем.
В последних версиях React Navigation используется другой формат конфигурации, более строгий в этом отношении:
- Форма конфига должна соответствовать форме вложенности в структуре навигации
- Только экраны, определенные в конфигурации, могут быть использованы для глубокого связывания.
Таким образом, приведенный выше конфиг следует рефакторить до следующего формата:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Здесь у объекта конфигурации появилось новое свойство screens
, а конфигурации Feed
и Profile
теперь вложены в Home
, чтобы соответствовать структуре навигации.
Если у вас используется старый формат, то он продолжит работать без изменений. Однако вы не сможете указать шаблон подстановочного знака для обработки несопоставленных экранов или предотвратить глубокую привязку экранов. Старый формат будет удален в следующем крупном релизе. Поэтому мы рекомендуем переходить на новый формат, когда это будет возможно.
Пример приложения¶
В примере приложения будет использован управляемый рабочий процесс Expo. Руководство будет посвящено созданию конфигурации глубокой перелинковки, а не созданию самих компонентов, но вы всегда можете ознакомиться с полной реализацией в github repo.
Прежде всего, необходимо определиться с навигационной структурой приложения. Если говорить просто, то основным навигатором будет навигатор нижних вкладок с двумя экранами. Первый экран будет представлять собой простой стековый навигатор, называемый HomeStack
, с двумя экранами: Home
и Profile
, а второй экран вкладок будет простым, без вложенных навигаторов, и будет называться Settings
:
1 2 3 4 5 |
|
После создания структуры навигации можно создать конфиг для глубокой привязки, который будет содержать отображения для каждого экрана на сегмент пути. Например:
1 2 3 4 5 6 7 8 9 10 11 |
|
Как видно, Home
и Profile
вложены в свойство screens
свойства HomeStack
. Это означает, что при передаче URL-адреса /home
он будет преобразован в объект состояния HomeStack
->Home
(аналогично для /user
это будет HomeStack
->Profile
). Вложенность этого объекта должна соответствовать вложенности наших навигаторов.
Здесь свойство HomeStack
содержит объект config. Конфигурация может быть сколь угодно глубокой, например, если Home
был навигатором, то можно сделать его объектом со свойством screens
и поместить внутрь него больше экранов или навигаторов, что сделает строку URL более читаемой.
А что если вы хотите, чтобы в качестве начального экрана в навигаторе использовался определенный экран? Например, если у вас есть URL, открывающий экран Home
, вы хотите иметь возможность перейти к Profile
с него, используя метод навигации navigation.goBack()
. Это возможно, если определить для навигатора initialRouteName
. Оно будет выглядеть следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 |
|