Solid Router¶
В современной веб-разработке маршрутизатор - это программный компонент, отвечающий за обработку клиентских запросов и определяющий, какой компонент следует отобразить, с помощью маршрутизации на стороне сервера или на стороне клиента. При поступлении запроса от клиента маршрутизатор оценивает URL и решает, какой контроллер или компонент на стороне сервера должен обработать этот запрос или быть отображен.
Solid Router - это простой и удобный в использовании универсальный маршрутизатор для приложений Solid. Он работает как на клиенте, так и на сервере. Маршруты задаются с помощью простого синтаксиса JSX и могут быть вложенными. Однако маршруты можно передавать и в виде объекта конфигурации.
Solid Router обладает множеством замечательных возможностей, одной из которых является поддержка вложенной маршрутизации, позволяющей изменять определенную часть компонента, а не заменять его полностью.
Он поддерживает все методы Solid SSR и имеет встроенные переходы Solid, поэтому его можно свободно использовать с суспензиями, ресурсами и "ленивыми" компонентами. Solid Router также позволяет определить функцию данных, которая загружается параллельно с маршрутами (render-as-you-fetch).
Дополнительную информацию о Solid Router можно найти на Solid Router GitHub page.
Начало работы¶
Для того чтобы начать работу с Solid Router, необходимо установить его в проект, поскольку по умолчанию он не установлен. После этого необходимо настроить маршрутизатор и определить некоторые маршруты.
Установка¶
Давайте перейдем к установке маршрутизатора. Для этого нам нужно установить маршрутизатор с помощью NPM, Yarn или вашего любимого менеджера пакетов.
1 2 3 4 5 | |
Настройка¶
Теперь, когда маршрутизатор установлен, мы можем его настроить и определить некоторые маршруты. Начнем с импорта маршрутизатора в наш корневой файл Index.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
Теперь, когда маршрутизатор настроен, мы можем определить некоторые маршруты. Начнем с того, что сделаем компонент App нашей домашней страницей или маршрутом /.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
В приведенном выше коде мы импортировали компоненты Route и Routes из Solid Router и определили маршрут для главной страницы. Компонент Route принимает свойство path, которое представляет собой путь URL, которому будет соответствовать маршрут. Свойство component - это компонент, который будет отображаться при совпадении с маршрутом. Компонент Routes используется для группировки маршрутов и необходим для вложенных маршрутов. Этот компонент используется для того, чтобы показать, где должны быть отображены маршруты.
Вы можете добавить несколько маршрутов в компонент Routes, и они будут отображаться каждый раз, когда URL-адрес будет соответствовать пути маршрута. Добавим маршрут для страниц /about и /contact.
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 | |
Примечание
Если вы хотите лениво загружать свои компоненты, чтобы они загружались только при совпадении маршрута, вы можете использовать функцию lazy из solid-js для ленивой загрузки своих компонентов.
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 26 27 | |
Теперь, когда определены маршруты, мы можем переходить на страницы /about и /contact. Давайте добавим несколько ссылок в компонент App, чтобы можно было переходить на другие страницы. Для добавления ссылок в маршруты можно использовать компонент A из Solid Router. Добавим ссылки на страницы /about и /contact.
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 26 27 28 | |
Создание ссылок на другие маршруты¶
Компонент A¶
Компонент A используется для создания ссылок на другие маршруты в вашем приложении. Компонент A также принимает свойство href, которое представляет собой URL-путь, по которому будет осуществляться переход по ссылке. Компонент A также принимает свойство activeClass, которое представляет собой класс, который будет применен к ссылке, когда текущий путь маршрута совпадет с путем ссылки.
Вот краткий пример того, как может выглядеть базовое приложение Solid с Solid Router и как использовать компонент A.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | |
Приведем пример использования компонента A и его свойства activeClass.
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 26 27 28 29 30 31 | |
Примечание
Будьте внимательны при использовании свойства activeClass, поскольку по умолчанию в соответствие включаются маршруты, являющиеся потомками или иным образом вложенными в совпадающий путь (например, /about будет соответствовать /about/me и /about/me/you). Однако можно использовать булево свойство `end для точного соответствия пути (например, /about будет соответствовать только /about). Свойство end особенно полезно для ссылок на корневой маршрут /, который будет соответствовать всем путям.
Вот список всех свойств, которые принимает компонент A.
| Свойства | Тип | Описание |
|---|---|---|
href | string | Путь маршрута, на который нужно перейти. Он будет разрешен относительно маршрута, в котором находится ссылка, но перед ним можно поставить /, чтобы вернуться к корню. |
activeClass | string | Класс, который будет применяться к ссылке, когда текущий путь маршрута совпадает с путем ссылки. |
end | boolean | Если true, то ссылка считается активной только тогда, когда текущее местоположение точно совпадает с href; если false, то проверяется, начинается ли текущее местоположение с href |
noScroll | boolean | Если true, то отключается стандартное поведение прокрутки к верху новой страницы |
replace | boolean | Если true, то не добавлять новую запись в историю браузера. (По умолчанию новая страница будет добавлена в историю браузера, поэтому нажатие кнопки назад приведет к переходу на предыдущий маршрут). |
state | unknown | Вставить это значение в стек истории при навигации |
inactiveClass | string | Класс, который должен отображаться, когда ссылка неактивна (когда текущее местоположение не соответствует ссылке) |
Компонент Navigate¶
Solid Router предоставляет компонент Navigate, который работает аналогично компоненту A, но сразу после отображения компонента осуществляет переход по указанному пути. Он также использует свойство href, но у вас есть дополнительная возможность передать в href функцию, возвращающую путь для перехода:
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 24 25 26 27 | |
В приведенном выше фрагменте кода мы добавили динамический маршрут с именем user, который принимает параметр :id. Параметр :id будет передаваться компоненту User через примитив useParams. Подробнее о примитивах Solid Router мы поговорим позже.
Приведем пример использования динамического маршрута в компоненте User.
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 26 27 28 29 30 31 32 33 | |
Для доступа к динамическим параметрам маршрута можно использовать объект params. В приведенном примере мы получили доступ к параметру id. Параметры можно использовать даже в таких примитивах, как createResource и createSignal:
Вот пример того, как это может выглядеть в примитиве createResource:
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 26 27 28 29 30 31 32 | |
В приведенном выше фрагменте кода мы передали параметр id примитиву createResource. Это означает, что функция fetchUser будет вызываться каждый раз при изменении параметра id. Это удобно, если необходимо получать новые данные о пользователе при каждом изменении параметра id.
Необязательные параметры¶
Вы также можете сделать параметр необязательным, добавив символ ? после имени параметра. Например, если вы хотите сделать параметр id необязательным, то сделайте следующее:
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 26 27 | |
Необязательный маршрут /user/:id? будет соответствовать /user и /user/123, но не /user/123/comment.
Маршруты/параметры диких символов¶
Маршруты с подстановочными знаками - это маршруты, которые соответствуют любому пути-потомку в пределах заданного пути.
Параметр также можно сделать подстановочным, добавив * после имени параметра. Например, если необходимо сделать параметр id подстановочным, то можно сделать следующее:
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 26 27 | |
Маршрут с подстановочным символом /user/* будет соответствовать маршрутам /user, /user/123, /user/123/comment, /user/123/comment/456 и т.д.
Если вы хотите получить доступ к параметру wildcard, вы можете назвать его:
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 26 27 | |
Доступ к параметру wildcard осуществляется так же, как и к параметру динамического маршрута:
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 26 27 28 29 30 31 32 33 | |
Примечание
Токен * должен быть последним в пути маршрута. Например, /user/*id является допустимым, а /user/id* и /user/*id/foo - нет.
Множественные пути¶
Маршруты также поддерживают определение нескольких путей с помощью массива. Это позволяет маршруту оставаться смонтированным и не перерисовываться при переключении между двумя или более местами, которым он соответствует:
1 2 | |
Функции данных¶
В предыдущих примерах компонент User загружается в ленивом режиме, а затем происходит выборка данных. С помощью функций данных маршрута мы можем начать получать данные параллельно с загрузкой маршрута, чтобы использовать их как можно быстрее.
Для этого создайте функцию, которая получает и возвращает данные с помощью createResource. Затем передайте эту функцию в свойство data компонента Route.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | |
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 26 27 | |
Функция data будет вызвана после загрузки маршрута, а доступ к результату можно получить, вызвав useRouteData в компоненте маршрута.
1 2 3 4 5 | |
1 2 3 4 5 | |
Поскольку функция data имеет только один аргумент, которым является объект, содержащий информацию о маршруте, например
| Ключ | Тип | Описание |
|---|---|---|
params | object | Параметры маршрута (то же значение, что и при вызове функции useParams() внутри компонента маршрута) |
location | { pathname, search, hash, query, state, key} | Объект, который можно использовать для получения дополнительной информации о пути (соответствует useLocation()) |
navigate | (to: string, options?: NavigateOptions) => void | Функция, которую можно вызвать для перехода к другому маршруту (соответствует useNavigate()) |
data | unknown | Данные, возвращаемые родительской функцией data, если таковая имеется. (Данные будут проходить через все промежуточные вложенности.) |
Общим шаблоном является экспорт функции данных, соответствующей маршруту, в специальный файл route.data.js|ts. Таким образом, функция данных может быть импортирована без загрузки чего-либо еще.
1 2 3 4 5 6 7 8 | |
Импортировать функцию fetchUser больше не нужно, поскольку она используется не здесь, а в файле [id].data.js.
Вложенные маршруты¶
Вложенные маршруты - это еще один способ определения маршрутов. Они полезны, когда у вас много маршрутов и вы хотите сгруппировать их вместе. Они также позволяют определить компонент макета, который будет использоваться для всех вложенных маршрутов.
Приведем небольшой пример кода, показывающий разницу между обычными и вложенными определениями маршрутов:
1 | |
1 2 3 | |
/users/:id отображает компонент <User/>, а /users/ - пустой маршрут.
Маршрут задается только для листовых узлов Route (внутренних компонентов Route). Если вы хотите сделать родительским свой собственный маршрут, то его необходимо указать отдельно:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
Первое определение Nested Route не будет работать так, как вы ожидаете, поскольку оно работает совершенно по-другому, используя компонент <Outlet/>. Компонент <Outlet/> используется для отображения дочерних маршрутов родительского маршрута. Компонент <Outlet/> отображается только тогда, когда активен родительский маршрут. Это означает, что компонент <User/> будет отображаться только при активном маршруте /users/:id. Вот как это выглядит:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
Конфигурация маршрутов остается прежней, но теперь элементы маршрута будут появляться внутри родительского элемента, в котором был объявлен <Outlet/>.
Вкладывать элементы можно до бесконечности - только помните, что только листовые узлы станут собственными маршрутами. В данном примере создан только один маршрут /layer1/layer2, и он отображается в виде трех вложенных div'ов.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | |
Если объявить функцию данных на родительском и дочернем маршрутах, то результат работы родительской функции данных будет передан в дочернюю функцию данных в качестве свойства data аргумента, как это было описано в предыдущем разделе. Это работает, даже если маршрут не является прямым дочерним, поскольку по умолчанию каждый маршрут пересылает данные своего родителя.
Маршрутизатор с хэш-режимом¶
По умолчанию Solid Router использует location.pathname в качестве пути маршрута. Вы можете просто переключиться в хэш-режим через свойство source компонента <Router>.
1 2 3 4 5 | |
Маршрутизация на основе конфигурации¶
Для определения маршрутов необязательно использовать JSX. Для определения маршрутов можно также использовать объект config. Это удобно, когда вы хотите определить маршруты в отдельном файле и импортировать их в приложение, а затем передать их в useRoutes.
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | |
Примитивы маршрутизатора¶
Solid Router предоставляет ряд примитивов, которые считывают контекст Router и Route.
useParams¶
Получает реактивный объект типа store, содержащий текущие параметры маршрута, определенные в Route.
1 2 3 4 5 6 | |
useNavigation¶
Этот метод извлекает метод для императивной навигации. Он возвращает функцию navigate, которая принимает путь и необязательный объект options. Объект options может содержать свойство replace, чтобы заменить текущую запись истории вместо того, чтобы проталкивать новую.
1 2 3 4 5 | |
Вот список доступных опций:
- resolve (boolean, по умолчанию
true): преобразовать путь к текущему маршруту - replace (boolean, по умолчанию
false): заменить запись в истории - scroll (boolean, по умолчанию
true): прокрутка к верху после навигации - state (any, по умолчанию
undefined): передавать пользовательское состояние в location.state
Примечание: Сериализация состояния осуществляется с использованием structured clone algorithm, который поддерживает не все типы объектов.
useLocation¶
Извлекает реактивный объект location, полезный для получения таких вещей, как pathname, search, hash, state и т. д.
1 2 3 4 5 | |
useSearchParams¶
Получает кортеж, содержащий реактивный объект для чтения параметров запроса текущего местоположения и метод для их обновления. Объект является прокси, поэтому для подписки на реактивные обновления необходимо получить доступ к свойствам. Обратите внимание, что значения будут строками, а имена свойств сохранят свой регистр.
Метод setter принимает объект, записи которого будут объединены в текущую строку запроса. Значения типа '', undefined и null будут удалять ключ из результирующей строки запроса. Обновления будут вести себя так же, как навигация, и сеттер принимает тот же необязательный второй параметр, что и navigate, а автопрокрутка по умолчанию отключена.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
useIsRouting¶
Получает сигнал, указывающий, находится ли маршрут в данный момент в состоянии перехода. Полезно для отображения состояния "застоялся/отложен", когда разрешение маршрута приостановлено во время параллельного рендеринга.
1 2 3 4 5 6 7 | |
useRouteData¶
Получает возвращаемое значение из функции data function.
1 2 3 4 5 | |
1 2 3 4 5 | |
useMatch¶
Функция useMatch принимает аксессор, возвращающий путь, и создает Memo, возвращающий информацию о совпадении, если текущий путь совпадает с предоставленным путем. Полезно для определения соответствия заданного пути текущему маршруту.
1 2 3 | |
useRoutes¶
Используется для определения маршрутов через объект конфигурации вместо JSX. См. раздел Маршрутизация на основе конфигурации.
useBeforeLeave¶
Функция useBeforeLeave принимает функцию, которая будет вызвана перед выходом из маршрута. Функция будет вызвана с:
from(Location): текущее местоположение (до изменения).to(string | number): путь, переданный для навигации.options(NavigateOptions): опции, передаваемые навигатору.preventDefault(void function): вызов блокировки изменения маршрута.defaultPrevented(readonly boolean): true, если все ранее вызванные обработчики оставления вызывали preventDefault().retry(void function, force?: boolean ): вызов для повторного выполнения той же навигации, возможно, после согласования с пользователем. Передайте true, чтобы пропустить повторный запуск обработчиков ухода (т.е. принудительный переход без подтверждения).
Пример использования:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
Более подробную информацию о Solid Router можно найти в репозитории Github здесь.