- Введение
- Выделенные особенности
- Перед продолжением
- Установка
- Использование
- Ленивая загрузка приложений
- Создание приложений
- Псевдоним пути приложений
- Декларация модулей приложений
- Структура модулей
- Концепция входа в модуль
- Определение списка приложений
- Определение маршрутов модулей
- Промежуточное программное обеспечение маршрута
- Группированные маршруты
- Базовый макет страницы
- Расширение базового макета
- Относительность маршрутов
- Структура пути маршрута
- Конфигурации маршрутизаторов
- Корневой компонент
- Навигация по ссылкам
- Компонент перенаправления
- Структура маршрутов
- Получение параметров маршрутизатора
- Дикая карта маршрута
- Более ограниченные сегменты маршрута
- Переключение на другой код локали
- Переход к маршруту
- Переход назад
- Обновление страницы
- Получение маршрута текущей страницы
- Получение полного url страницы
- Конкатенация маршрутов
- Обновление строки запроса
- Получение текущего хэш-значения в url
- Получение строки запроса
- Получение базового url проекта
- Получить текущее направление страницы
- Direction Is
- Получить предыдущий маршрут
- События маршрутизатора
- Заключение
Введение
Этот пакет предназначен для того, чтобы сделать ваш код элегантным, простым в обслуживании и разделить ваш проект на несколько приложений и модулей.
Представьте, что у вас есть проект, который содержит панель администратора и фронт-офис сайта, Mongez React Router (MRR) позволит нескольким командам/членам работать отдельно в каждом приложении, и оба приложения будут находиться в одном проекте без каких-либо конфликтов, кроме того, вы можете совместно использовать общие стили/активы.
Выделенные особенности
- ✅ Объявление маршрутов более читабельным и чистым способом.
- ✅ Общие базовые макеты для нескольких маршрутов, что позволяет избежать повторного рендеринга для таких частей, как верхний и нижний колонтитулы.
- ✅ Простые определения Middleware
- ✅ Ленивая загрузка для целых приложений и модулей для уменьшения размера производственного пакета.
- ✅ Группировка маршрутов с общими функциями, такими как установка базового пути, общее промежуточное ПО между маршрутами.
- ✅ Переключение языков без перезагрузки страницы.
- ✅ Опциональное обновление той же страницы при повторном переходе на нее.
- ✅ Множество помощников для перехода между маршрутами с помощью функций.
- ✅ Работа с функциями для навигации вместо хуков.
- ✅ Возможность перенаправления на маршрут «Страница не найдена» или просто отрисовка компонента.
- ✅ Определяет прелоадер (например, Progress Bar), пока не загрузится пакет приложений/модулей.
- ❌ Отсутствие некрасивой записи маршрутов в компонентах.
Перед продолжением
Эта документация проиллюстрирует возможности пакета, однако рекомендуется использовать его вместе с Mongez React для лучшей организации проекта.
Под капотом используется React Router DOM.
Установка
yarn add @mongez/react-router
Или
npm i @mongez/react-router
Использование
В вашем файле src/index
импортируйте пакет и очистите секцию ReactDOM.render
.
// src/index.ts
import router from "@mongez/react-router";
// remove the following code from the file
import ReactDOM from "react-dom";
import App from "./App";
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById("root")
);
Теперь давайте добавим маршрут для нашей главной страницы
// src/index.ts
import router from "@mongez/react-router";
import HomePage from "./Home";
router.add("/", HomePage);
// Start scanning for all of registered routes
router.scan();
Мы импортировали наш компонент HomePage
, который является обычным компонентом react, затем мы использовали метод router.add
для определения нашего первого маршрута, который определяет маршрут нашей домашней страницы.
Затем мы вызвали router.scan()
, чтобы начать сканирование всех зарегистрированных маршрутов в маршрутизаторе для вызова нужного маршрута.
Пожалуйста, не забудьте установить
router.scan
после объявления всех ваших маршрутов.
Ленивая загрузка приложений
Одна из самых полезных функций в этом инструменте — ленивая загрузка приложений, особенно если у вас несколько приложений в одном проекте, поэтому давайте посмотрим, как это можно сделать.
Например, ни один из файлов admin
не будет загружен, пока пользователь не перейдет к маршруту /admin
в браузере, а также любой модуль внутри администратора, например, модуль administrators
.
Создание приложений
Любой проект react может содержать одно или несколько приложений в одном проекте, например, front-office
для основного сайта и admin
dashboard для управления сайтом.
Итак, давайте создадим эти два приложения в директории src/apps
, чтобы все выглядело следующим образом:
|--- src
|--- apps
|-- front-office
|-- admin
|--- index.ts
Псевдоним пути приложений
Чтобы заставить работать приложения с ленивой загрузкой, нам нужно определить абсолютный путь для наших приложений, используя файл tsconfig.json или используя link-module-alias, что более рекомендуется.
Если вы используете link-module-alias
, откройте файл package.json
и добавьте в него следующий код:
"scripts": {
"postinstall": "link-module-alias"
},
"_moduleAliases": {
"apps": "src/apps"
}
Затем запустите yarn postinstall
или npm run postinstall
.
Пожалуйста, помните, что при обновлении любого пакета не забудьте выполнить эту команду снова.
Если вы устанавливаете новый пакет, эта команда будет выполняться автоматически, поэтому вам не нужно будет ее выполнять.
Еслиapps
не определен в псевдониме пути, ленивая загрузка приложений не будет работать и вызовет ошибку.
Декларация модулей приложений
Для нормальной работы каждое приложение должно иметь как минимум два файла.
front-office-modules.json
для определения структуры приложения, модулей и его внутренних маршрутов.- Файл
front-office-provider.ts
в качестве точки входа в приложение, так как он будет вызываться в самом начале перед вызовом любого внутреннего модуля внутри приложения.
Имя файла modules.json должно быть в такой последовательности
appName-modules.json
, как мы назвали наш файл модулей администратора.
Провайдер приложения должен быть названappName-provider.ts
илиappName-provider.js
, если вы используете Javascript.
|--- src
|--- apps
|-- front-office
|-- front-office-modules.json
|-- front-office-provider.ts
|-- admin
|--- index.ts
Теперь откройте файл front-office-modules.json
и поместите в него следующий код.
{
"name": "front-office",
"path": "/",
"modules": [
{
"entry": ["/"],
"module": "home"
}
]
}
Давайте подробно рассмотрим каждый ключ в этом файле.
Структура модулей
Каждое приложение состоит из списка модулей, каждый модуль должен иметь как минимум файл provider
.
Файл provider.ts|.js
будет вызван непосредственно, когда браузер перейдет по пути входа в модуль, что будет показано позже. В этом файле должны быть импортированы настройки нашего модуля, например, его маршруты.
Концепция входа в модуль
Давайте рассмотрим пример для наглядности.
Допустим, мы работаем над проектом интернет-магазина, который будет содержать панель счетов клиентов, эта панель может иметь следующие маршруты:
/account/dashboard
/account/edit-profile
/account/order-history
/account/order-history/:id
Все предыдущие маршруты являются частью модуля account
в нашем проекте, также все они начинаются с сегмента /account
.
В этом случае структура нашего проекта будет выглядеть следующим образом:
|--- src
|--- apps
|-- front-office
|-- account
|-- components
|-- DashboardPage.tsx
|-- EditProfilePage.tsx
|-- OrderHistoryList.tsx
|-- SingleOrderHistory.tsx
|-- front-office-modules.json
|-- front-office-provider.ts
|-- admin
|--- index.ts
В этом смысле, все они имеют один модуль account
и все они начинаются с /account
, чтобы объявить это в нашем front-office-modules.json
мы добавим только начальный сегмент всех этих маршрутов /account
.
В результате Mongez React Router загрузит модуль
account
, когда увидит, что маршрут начинается с/account
.
{
"name": "front-office",
"path": "/",
"modules": [
{
"entry": ["/account"],
"module": "account"
},
{
"entry": ["/"],
"module": "home"
}
]
}
Еще одна вещь, о которой следует упомянуть, заключается в том, что мы также можем загрузить модуль с различными маршрутами, например, если /login
является частью нашего модуля account
, то мы можем добавить его в раздел entry
.
{
"name": "front-office",
"path": "/",
"modules": [
{
"entry": ["/account", "/login"],
"module": "account"
},
{
"entry": ["/"],
"module": "home"
}
]
}
Ключ entry
принимает только первый сегмент маршрута, поэтому не определяйте внутри него весь маршрут.
✅
{
"name": "front-office",
"path": "/",
"modules": [
{
"entry": ["/account", "/login"],
"module": "account"
},
{
"entry": ["/"],
"module": "home"
}
]
}
❌
{
"name": "front-office",
"path": "/",
"modules": [
{
"entry": ["/account/dashboard", "/account/edit-profile", "/login"],
"module": "account"
},
{
"entry": ["/"],
"module": "home"
}
]
}
Определение списка приложений
Теперь давайте создадим файл src/shared/apps-list.ts
, чтобы задать список приложений.
Для лучшей организации мы создадим каталог
shared
внутри нашегоsrc
, чтобы мы могли задать общие конфигурации для нескольких приложений.
|--- src
|--- apps
|-- front-office
|-- front-office-modules.json
|-- front-office-provider.ts
|-- admin
|--- shared
|-- apps-list.ts
|--- index.ts
// src/shared/apps-list.ts
import { setApps } from "@mongez/react-router";
import frontOfficeApp from "apps/front-office/front-office-modules.json";
setApps([frontOfficeApp]);
Мы использовали псевдоним
apps/
напрямую, так как уже использовали псевдоним path.
Теперь давайте вернемся к нашему индексному файлу и импортируем наш файл apps-list.ts
.
// src/index.ts
import "./shared/apps-list";
import router from "@mongez/react-router";
router.scan();
С этого момента вы можете лениво загружать любое новое приложение или любой новый модуль, создав его каталог и провайдера.
Определение маршрутов модулей
Теперь мы импортировали наш список приложений, и все работает хорошо, за исключением того, что ни один маршрут не будет загружен!
Причина этого в том, что мы не объявили никаких маршрутов, поскольку мы только сообщили MRR
для загрузки провайдера модуля, поэтому теперь нам нужно определить наши маршруты для нашего модуля.
В src/apps/front-office/home
у нас должно быть два файла: provider.ts
и routes.ts
.
// src/apps/front-office/home/provider.ts
import "./routes";
Мы только что импортировали наш файл routes.ts
, теперь давайте добавим туда наши маршруты.
// src/apps/front-office/home/routes.ts
import router from "@mongez/router";
import HomePage from "./components/HomePage";
router.add("/", HomePage);
Теперь мы готовы к работе, так как закончили нашу настройку.
Промежуточное программное обеспечение маршрута
Например, посетитель не может получить доступ к приборной панели своего аккаунта, пока не войдет в систему, в таком случае мы можем использовать промежуточное ПО.
В файле src/apps/front-office/account/routes.ts
мы можем определить наши маршруты следующим образом:
// src/apps/front-office/account/routes.ts
import router from "@mongez/react-router";
import AccountDashboardPage from "./components/DashboardPage";
import EditProfilePage from "./components/EditProfilePage";
import OrderHistoryPage from "./components/OrderHistoryPage";
import SingleOrderHistoryPage from "./components/SingleOrderHistoryPage";
import Guardian from "./middleware/Guardian";
router.add("/account", AccountDashboardPage, [Guardian]);
router.add("/account/edit-profile", EditProfilePage), [Guardian];
router.add("/account/order-history", OrderHistoryPage, [Guardian]);
router.add("/account/order-history/:id", SingleOrderHistoryPage, [Guardian]);
Здесь мы определили наши маршруты, с новым аргументом в router.add
, который представляет собой массив промежуточных элементов, которые будут объявлены перед переходом на наши страницы.
Теперь давайте посмотрим на наш новый файл промежуточного ПО Guardian.
// src/apps/front-office/account/middleware/Guardian.tsx
import user from "somewhere-in-the-app";
import React from "react";
import { Redirect } from "@mongez/react-router";
export default function Guardian() {
if (user.isNotLoggedIn()) {
return <Redirect to="/login" />;
}
return null;
}
Здесь мы определили компонент, который позволяет нам проверить, не вошел ли пользователь в систему, затем мы перенаправим пользователя на маршрут входа, используя компонент Redirect
из MRR
.
Теперь, когда пользователь попадает на любой из маршрутов учетных записей, сначала будет вызван компонент Guardian
, если пользователь не вошел в систему, то вместо компонента страницы будет вызван компонент перенаправления.
Если промежуточное ПО вернуло значение, то оно будет отображено вместо компонента страницы.
Таким образом, промежуточное ПО может выглядеть следующим образом:
// src/apps/front-office/account/middleware/Guardian.tsx
import user from "somewhere-in-the-app";
import React from "react";
export default function Guardian() {
if (user.isNotLoggedIn()) {
return <h1>You do not have access to this page, please login first.</h1>;
}
return null;
}
Группированные маршруты
Поскольку мы можем использовать метод router.add
для определения маршрута, мы можем определить один или несколько маршрутов с общими параметрами, такими как префикс или промежуточное ПО.
В нашем предыдущем примере с промежуточным ПО мы видим, что все маршруты начинаются с /account
и все они имеют одинаковое промежуточное ПО, мы можем сгруппировать эти маршруты в один метод, используя метод router.group
.
// src/apps/front-office/account/routes.ts
import router from "@mongez/react-router";
import AccountDashboardPage from "./components/DashboardPage";
import EditProfilePage from "./components/EditProfilePage";
import OrderHistoryPage from "./components/OrderHistoryPage";
import SingleOrderHistoryPage from "./components/SingleOrderHistoryPage";
import Guardian from "./middleware/Guardian";
router.group({
path: "/account",
middleware: [Guardian],
routes: [
{
path: "/",
component: AccountDashboardPage,
},
{
path: "/edit-profile",
component: EditProfilePage,
},
{
path: "/order-history",
component: OrderHistoryPage,
},
{
path: "/order-history/:id",
component: SingleOrderHistoryPage,
},
],
});
Теперь наш код стал компактнее и чище, также вы можете передать дополнительное промежуточное ПО любому объекту маршрута, если хотите добавить больше промежуточного ПО к определенным маршрутам.
Префикс в методе group будет склеен со всеми маршрутами в массиве routes, поэтому маршрут AccountDashboardPage
будет /account/
, но последние /
будут обрезаны MRR
.
Вы можете задать путь
AccountDashboardPage
в виде пустой строки » это тоже работает.
Базовый макет страницы
Большинство приложений имеют одинаковую структуру макета, такую как верхний и нижний колонтитулы, среди которых находится содержимое страницы.
Это можно легко сделать с помощью MRR
, используя метод router.partOf
.
// src/apps/front-office/components/BaseLayout.tsx
import React from "react";
import Header from "./Header";
import Footer from "./Footer";
export default function BaseLayout({ children }) {
return (
<>
<Header />
<main>{children}</main>
<Footer />
</>
);
}
Теперь давайте добавим наш новый базовый макет на нашу Домашнюю страницу.
// src/apps/front-office/home/routes.ts
import router from "@mongez/router";
import HomePage from "./components/HomePage";
import BaseLayout from "apps/front-office/components/BaseLayout";
router.partOf(BaseLayout, [
{
path: "/",
component: HomePage,
},
]);
Теперь нашему компоненту HomePage
не нужно вызывать верхний или нижний колонтитул страницы, теперь он является частью BaseLayout
.
Это может быть полезно и для router.group
, мы можем установить общий макет для списка страниц.
Давайте вернемся к нашему модулю счета.
// src/apps/front-office/account/routes.ts
import router from "@mongez/react-router";
import AccountDashboardPage from "./components/DashboardPage";
import EditProfilePage from "./components/EditProfilePage";
import OrderHistoryPage from "./components/OrderHistoryPage";
import SingleOrderHistoryPage from "./components/SingleOrderHistoryPage";
import Guardian from "./middleware/Guardian";
import BaseLayout from "apps/front-office/components/BaseLayout";
router.group({
path: "/account",
layout: BaseLayout,
middleware: [Guardian],
routes: [
{
path: "/",
component: AccountDashboardPage,
},
{
path: "/edit-profile",
component: EditProfilePage,
},
{
path: "/order-history",
component: OrderHistoryPage,
},
{
path: "/order-history/:id",
component: SingleOrderHistoryPage,
},
],
});
Теперь мы добавили новое свойство в объект group под названием layout
, которое определяет макет, который будет отображать все страницы маршрутов.
Расширение базового макета
Давайте рассмотрим другой сценарий, когда страницы аккаунтов имеют общую боковую панель между всеми страницами, мы можем сделать новый макет, который будет содержать верхний колонтитул, нижний колонтитул и боковую панель аккаунта.
// src/apps/front-office/account/components/AccountLayout.tsx
import React from "react";
import Header from "apps/front-office/components/Header";
import Header from "apps/front-office/components/Footer";
import AccountSidebar from "./AccountSidebar";
export default function AccountLayout({ children }) {
return (
<>
<Header />
<main>
<AccountSidebar />
<div>{children}</div>
</main>
<Footer />
</>
);
}
Теперь давайте снова перейдем к маршрутам нашего аккаунта, чтобы использовать наш новый макет вместо базового.
// src/apps/front-office/account/routes.ts
import router from "@mongez/react-router";
import AccountDashboardPage from "./components/DashboardPage";
import EditProfilePage from "./components/EditProfilePage";
import OrderHistoryPage from "./components/OrderHistoryPage";
import SingleOrderHistoryPage from "./components/SingleOrderHistoryPage";
import Guardian from "./middleware/Guardian";
import AccountLayout from "./components/AccountLayout";
router.group({
path: "/account",
layout: AccountLayout,
middleware: [Guardian],
routes: [
{
path: "/",
component: AccountDashboardPage,
},
{
path: "/edit-profile",
component: EditProfilePage,
},
{
path: "/order-history",
component: OrderHistoryPage,
},
{
path: "/order-history/:id",
component: SingleOrderHistoryPage,
},
],
});
Теперь мы почти закончили, осталось расширить наш базовый макет, поскольку мы снова ввели верхний и нижний колонтитулы в компонент AccountLayout
, давайте это исправим.
// src/apps/front-office/account/components/AccountLayout.tsx
import React from "react";
import BaseLayout from "apps/front-office/components/BaseLayout";
import AccountSidebar from "./AccountSidebar";
export default function AccountLayout({ children }) {
return (
<BaseLayout>
<AccountSidebar />
<div>{children}</div>
</BaseLayout>
);
}
Теперь наш код очень аккуратен и может легко поддерживаться.
Относительность маршрутов
Все маршруты, определенные внутри файлов routes.ts
в режиме ленивой загрузки, имеют префикс пути к приложению, поэтому если у нас есть маршрут в админке, например /admin/login
, то определенный маршрут в src/apps/admin/administrators/routes.ts
будет /login
без добавления /admin
в начале.
// src/apps/admin/administrators/routes.ts
import LoginPage from "./components";
import router from "@mongez/react-router";
// here we'll define the route as /login not /admin/login
router.add("/login", LoginPage);
Структура пути маршрута
Основываясь на конфигурациях приложений, мы создали X структур маршрутов.
Таким образом, наша полная структура маршрута будет выглядеть примерно так:
/localeCode(optional)/app-path/route
Когда вы определяете маршрут, не добавляйте путь приложения или код локали, например:
✅
❌
Конфигурации маршрутизаторов
MRR
не требует установки каких-либо конфигураций, но рекомендуется определить некоторые конфигурации, например, код локали, используемый в проекте.
В директории src/shared
создадим новый файл config.ts
.
// src/shared/config.ts
import { setRouterConfigurations } from "@mongez/react-router";
setRouterConfigurations({
// if your app is multilingual then define all locale codes in the app
localeCodes: ["en", "ar"],
// if the production build will be in a directory and not the root, then define the directory path in basePath
basePath: "/",
});
Теперь импортируем файл в индексный файл.
// src/index.ts
// its important to import the config file before any route functions.
import "./shared/config";
import "./shared/apps-list";
import router from "@mongez/react-router";
router.scan();
Если вы используете Mongez React, это может быть частью конфигураций всего приложения.
Вот полный список доступных конфигураций
/**
* Router configuration options list
*/
type RouterConfigurations = {
/**
* Default locale code
*/
defaultLocaleCode?: string;
/**
* Locale codes list
*/
localeCodes?: string[];
/**
* Router preloader that will be displayed until the module is loaded
*
* @default React.Fragment
*/
preloader?: React.ComponentType<any>;
/**
* If set to true, the current layout will not be unmounted and the preloader (if set) will be displayed before it
* Please note the of the base layout and the preloader will have position `relative`
* This feature is still experimental and can be changed in future versions
* @experimental
* @default false
*/
preloadOverlay?: boolean;
/**
* App base path in production
*
* @default: /
*/
basePath?: string;
/**
* Determine whether to re-render the page
* When navigating to any page, even same current page
*
* Please note that can not be changed during the application is running
* as its value is cached at the application bootstrap
*
* @default: true
*/
forceRefresh?: boolean;
/**
* Scroll to top of the page when rendering new page
*
* @default true
*/
scrollTop?: boolean;
/**
* Top Root component that will wrap the entire application regardless the lazy module
*/
rootComponent?: React.ComponentType<any>;
/**
* NotFound Options
*/
notFound?: {
/**
* Not found mode
* The redirect mode will redirect the client to the path
*
* Please note that can not be changed during the application is running
* as its value is cached at the application bootstrap
*
* @default: render
*/
mode?: "redirect" | "render";
/**
* The route that will be redirected when the page is not found
* Works only when the mode is set to redirect
*
* @default: /404
*/
route?: string;
/**
* The component that will be rendered when the page is not found
* Works only when the mode is set to render
*
* @default: React.Fragment
*/
component?: React.ComponentType<any>;
};
};
Корневой компонент
Корневой компонент будет обертывать все приложение, независимо от того, загружается ли текущий маршрут в ленивом режиме или нет.
Корневой компонент не получает никаких реквизитов вообще.
Навигация по ссылкам
Использование компонента Link
в react предоставляет некоторые интересные возможности, чтобы сделать ссылку более читаемой и удобной в использовании.
// src/apps/front-office/home/components/HomePage.tsx
import React from "react";
import { Link } from "@mongez/react";
export default function HomePage() {
return (
<div>
<Link to="/account">Go To Account Page</Link>
</div>
);
}
Проще говоря, простая навигация по маршруту с помощью реквизита to
или href
.
Чтобы перейти к маршруту в новой вкладке:
<Link to="/account" newTab>
Go To Account Page In New Tab
</Link>
// outputs: <a href="/account" target="_blank" rel="noopener noreferrer">
Чтобы перейти к маршруту в другой локали, введите код:
Чтобы перейти к маршруту в другом приложении:
<Link to="/customers/100" app="admin">
Go To Customer page in admin app.
</Link>
// outputs: /admin/customers/100
<Link to="/account" localeCode="ar">
Go To Account Page With Arabic Locale Code
</Link>
// outputs: /ar/account
Переход к другому приложению с кодом локали
<Link to="/account" app="admin" localeCode="ar">
Go To Account Page In Admin App With Arabic Locale Code
</Link>
// outputs: /ar/admin/account
Реквизит app принимает имя приложения, а не путь к нему.
Чтобы перейти к url, просто задайте url :p.
<Link to="https://google.com">Go To Google</Link>
Сделать ссылку в виде электронной почты:
<Link mailTo="hassanzohdy@gmail.com">Email As Link</Link>
// outputs: <a href="mailto:hassanzohdy@gmail.com" .. />
или используя компонент MailLink
напрямую
import { MailLink } from "@mongez/react-router";
<MailLink to="hassanzohdy@gmail.com">Email As Link</MailLink>;
// outputs: <a href="mailto:hassanzohdy@gmail.com" .. />
Сделайте ссылку в виде телефонного номера:
<Link tel="+201002221122">Phone Number As Link</Link>
// outputs: <a href="tel:+201002221122" .. />
или используя компонент MailLink
напрямую
import { TelLink } from "@mongez/react-router";
<TelLink to="+201002221122">Phone Number As Link</TelLink>;
// outputs: <a href="tel:+201002221122" .. />
Конечно, вы можете отправить любые другие реквизиты html, такие как className
, id
и так далее.
Использовать внешнюю ссылку
import { ExternalLink } from "@mongez/react-router";
<ExternalLink to="https://google.com">Google</ExternalLink>;
// outputs: <a href="https://google.com">Google</a>
Открыть в новой вкладке
import { ExternalLink } from "@mongez/react-router";
<ExternalLink newTab to="https://google.com">
Google
</ExternalLink>;
// outputs: <a target="_blank" rel="noopener noreferrer" href="https://google.com">Google</a>
Компонент перенаправления
Этот компонент обычно используется с промежуточным ПО, как упоминалось ранее в разделе «Промежуточное ПО».
// src/apps/admin/account/middleware/Guardian.tsx
import user from "somewhere-in-the-app";
import React from "react";
import { Redirect } from "@mongez/react-router";
export default function Guardian() {
if (user.isNotLoggedIn()) {
return <Redirect to="/login" localeCode="fr" app="admin" />;
}
return null;
}
Предыдущий опекун перейдет к /admin/fr/login
, так как путь к приложению admin
— /admin
, после него добавляется код локали и, наконец, сам маршрут.
Структура маршрутов
Существует 4 типа маршрутов, для наглядности рассмотрим маршрут /login
.
- Маршрут в базовом приложении: конечный маршрут:
/login
, который построен какpage-route
. - Маршрут в базовом приложении с кодом локали: final route:
/login
, который строится как/locale-code/page-route
. - Маршрут в другом приложении, например, admin:
/admin/login
, который строится как/app-path/page-route
. - Маршрут в другом приложении с кодом локали, например admin:
/admin/ar/login
, который строится как/app-path/locale-code/page-route
.
Получение параметров маршрутизатора
Давайте возьмем сложный маршрут и посмотрим, как мы можем принимать его значения.
Наш путь маршрута будет таким: /admin/en/customers/101
и будет отображаться в компоненте CustomerPage
.
Этот путь маршрута определен как /customers/:id
.
// src/apps/admin/customers/routes.ts
import router from "@mongez/react-router";
import CustomerPage from "./components/CustomerPage";
router.add("/customers/:id", CustomerPage);
Теперь перейдем к нашему компоненту CustomerPage
.
// src/apps/admin/customers/components/CustomerPage.tsx
import React from "react";
export default function CustomerPage({ params }) {
const { localeCode, id } = params;
console.log(localeCode); // en
console.log(id); // 101
return <div>// component content</div>;
}
Сегмент /admin
будет проигнорирован, мы можем получить только localeCode
, который определяется MRR
внутренне, а наш определенный сегмент /:id
преобразуется в id
из объекта params.
Дикая карта маршрута
Давайте рассмотрим другой сценарий, в котором у нас есть динамические маршруты, например:
/categories/electronics/smart-phones/tablets
Маршрут определяет дерево категорий, поскольку мы будем обращаться к странице категории планшетов.
В этом смысле маршрут может быть чем-то другим, например /categories/electronics/smart-phones
, где мы перейдем к категории Smart Phones.
Для получения динамического маршрута мы можем использовать подстановочные знаки.
// src/apps/front-office/categories/routes.ts
import router from "@mongez/react-router";
import CategoryPage from "./components/CategoryPage";
router.add("/categories/:slug(.+)", CategoryPage);
В нашем компоненте CategoryPage
// src/apps/front-office/categories/components/CategoryPage.tsx
import React from "react";
export default function CategoryPage({ params }) {
const { slug } = params;
console.log(slug); // /electronics/smart-phones
return <div>// component content</div>;
}
Вы также можете использовать помощник dynamicSegment
для большей читабельности.
// src/apps/front-office/categories/routes.ts
import router, { dynamicSegment } from "@mongez/react-router";
import CategoryPage from "./components/CategoryPage";
router.add("/categories/" + dynamicSegment("slug"), CategoryPage);
Достигнет того же результата.
Более ограниченные сегменты маршрута
Иногда сегменты, такие как :id
, обычно содержат только целые числа, поэтому мы можем определить маршрут, который принимает только целые значения в маршруте, чтобы нам не пришлось делать еще один шаг проверки на заданный id, мы можем использовать помощник integerSegment
.
// src/apps/admin/customers/routes.ts
import router, { integerSegment } from "@mongez/react-router";
import CustomerPage from "./components/CustomerPage";
router.add("/customers/" + integerSegment("id"), CustomerPage);
Теперь, если пользователь перейдет по ссылке /customers/some-text
, он будет автоматически перенаправлен на страницу not found, хотя маршрут /customers/101
будет действительным.
Другая вспомогательная функция floatSegment
может быть использована для значений с плавающей точкой.
Переключение на другой код локали
Мы можем переключиться на другой код локали с помощью функции switchLang
.
import { switchLang } from '@mongez/react-router';
import React from 'react'
export default function Header() {
const onClick = e => {
switchLang('ar'); // refreshes the page and changes the locale code
};
return (
<div>
<button onClick={changeLocaleCode}>Switch To Arabic<button>
</div>
)
}
Переход к маршруту
Функция navigateTo
является одной из самых мощных функций, позволяющих перейти на другую страницу.
import { navigateTo } from "@mongez/react-router";
import React from "react";
export default function CreateAccountPage() {
const createAccount = (e) => {
axios.post("/register", { email, password }).then((response) => {
navigateTo("/home");
});
};
return (
<div>
<form onSubmit={createAccount}>
...
<button>Create a new account</button>
</form>
</div>
);
}
Переход с помощью кода локали
navigateTo("/login", "en"); // /en/login
Переход к маршруту с кодом локали и приложения.
navigateTo("/login", "en", "admin"); // /admin/en/login
Обратите внимание, что третий аргумент принимает имя приложения, а не путь к нему, если вы хотите использовать путь к приложению, просто добавьте его в первый аргумент.
Если проект имеет несколько кодов локали, то при навигации с помощью функцииnavigateTo
будет добавляться код локали, напримерnavigateTo('/login')
и код локали по умолчаниюen
, это приведет к навигации на/en/login
.
Переход назад
import { navigateBack } from "@mongez/react-router";
import React from "react";
export default function LoginPage() {
const login = (e) => {
axios.post("/login", { email, password }).then((response) => {
navigateBack();
});
};
return (
<div>
<form onSubmit={login}>
...
<button>Login</button>
</form>
</div>
);
}
Обычно используется на странице входа в систему, так как мы можем вернуть пользователя обратно после успешного входа в систему.
Обновление страницы
Мы можем обновить страницу, чтобы она снова отобразилась, используя функцию refresh
, это не приведет к полной перезагрузке страницы, но будет действовать как навигация по маршруту.
import { refresh } from '@mongez/react-router';
import React from 'react'
export default function Header() {
const refreshPage = () => {
refresh();
}
return (
<div>
<button onClick={refreshPage}>Refresh The Page<button>
</div>
)
}
Получение маршрута текущей страницы
Для получения маршрута текущей страницы можно использовать функцию currentRoute
.
// src/apps/front-office/front-office-provider.ts
import { currentRoute } from "@mongez/react-router";
// detect current route
console.log(currentRoute()); // will be something like /login or /account
Получение полного url страницы
Чтобы получить полный путь к странице, можно использовать функцию url
.
// src/apps/front-office/front-office-provider.ts
import { url } from "@mongez/react-router";
// detect current route
console.log(url()); // will be something like https://sitename.com/online-store/account
Конкатенация маршрутов
Иногда вам может понадобиться объединить несколько маршрутов в один маршрут, например, добавить маршрут из переменной в другую переменную, чтобы создать совершенно новый маршрут. К счастью, вы можете использовать вспомогательную функцию concatRoute
, которая сделает это за вас.
import { concatRoute } from "@mongez/react-router";
const localeCode = "ar";
const route = "login";
const appPath = "/admin";
const fullRoutePath = concatRoute(appPath, localeCode, route); // /admin/ar/login
Каждый аргумент может начинаться с косой чертой или без нее, также любые удвоенные косые черты будут преобразованы в одну косую черту, а если есть конечная косая черта, то она также будет обрезана.
import { concatRoute } from "@mongez/react-router";
const localeCode = "ar";
const route = "//login//";
console.log(localeCode, route); // /ar/login
Обновление строки запроса
Мы также можем обновить строку запроса в url с/без перехода к маршруту с новой строкой запроса, используя вспомогательную функцию updateQueryString
. Это может быть полезно в таких случаях, как фильтрация, поскольку мы можем обновить только маршрут без полного повторного рендеринга страницы.
import { updateQueryString } from "@mongez/react-router";
export default function FilterData() {
const filter = (e) => {
// Just dummy data for demo only
const filterData = {
name: "",
email: "",
age: 0,
published: true,
};
axios.get("/filter", filterData).then((response) => {
updateQueryString(filterData);
});
};
return (
<div>
<form onSubmit={filter}>
...
<button>Filter</button>
</form>
</div>
);
}
Если мы хотим перейти на ту же страницу с обновленной строкой запроса, мы можем установить второй аргумент в true.
import { updateQueryString } from "@mongez/react-router";
export default function FilterData() {
const filter = (e) => {
// Just dummy data for demo only
const filterData = {
name: "",
email: "",
age: 0,
published: true,
};
axios.get("/filter", filterData).then((response) => {
updateQueryString(filterData, true); // update and navigate
});
};
return (
<div>
<form onSubmit={filter}>
...
<button>Filter</button>
</form>
</div>
);
}
Получение текущего хэш-значения в url
Если в url есть значение #hash
, мы можем получить его с помощью вспомогательной функции hash
.
import { hash } from "@mongez/react-router";
// if the url is something like https://site-name.com/online-store/products/10#comments
console.log(hash()); // comments
Чтобы получить значение с хэшем, передайте функции аргумент со значением true.
import { hash } from "@mongez/react-router";
// if the url is something like https://site-name.com/online-store/products/10#comments
console.log(hash(true)); // #comments
Получение строки запроса
Чтобы получить значение из строки запроса, мы можем использовать вспомогательную функцию queryString
, эта функция возвращает три внутренних метода.
Чтобы получить значение ключа из строки запроса, можно использовать метод get
:
import { queryString } from "@mongez/react-router";
// if the url is something like https://site-name.com/online-store/products?sortBy=price&categoryId=10
console.log(queryString().get("sortBy")); // price
console.log(queryString().get("categoryId")); // 10
Обратите внимание, что если вы собираетесь получить несколько значений из строки запроса, инициируйте queryString()
в переменной, а затем используйте эту переменную.
import { queryString } from "@mongez/react-router";
// if the url is something like https://site-name.com/online-store/products?sortBy=price&categoryId=10
const params = queryString();
console.log(params.get("sortBy")); // price
console.log(params.get("categoryId")); // 10
Вы также можете получить значение по умолчанию, если параметр запроса не существует в url.
import { queryString } from "@mongez/react-router";
// if the url is something like https://site-name.com/online-store/products?sortBy=price&categoryId=10
const params = queryString();
console.log(params.get("sortBy")); // price
console.log(params.get("sortDirection", "desc")); // desc
Чтобы получить все параметры строки запроса в объекте, можно использовать метод all
.
import { queryString } from "@mongez/react-router";
// if the url is something like https://site-name.com/online-store/products?sortBy=price&categoryId=10
console.log(queryString().all()); // {sortBy: price, categoryId: 10}
Чтобы получить его в виде строки, используйте метод toString
.
import { queryString } from "@mongez/react-router";
// if the url is something like https://site-name.com/online-store/products?sortBy=price&categoryId=10
console.log(queryString().toString()); // sortBy=price&categoryId=10
Каждый раз, когда вы вызываете
queryString()
, он начинает собирать значения из значения параметров запроса, поэтому помните, чтобы не кэшировать его значение, если параметры строки запроса будут изменены, вам нужно будет вызвать метод снова.
✅
// src/front-office/products/components/ProductsListPage.tsx
import { queryString } from "@mongez/react-router";
// if the url is something like https://site-name.com/online-store/products?sortBy=price&categoryId=10
console.log(queryString().toString()); // sortBy=price&categoryId=10
import React from "react";
export default function ProductsListPage() {
const params = queryString();
console.log(params.get("sortBy")); // price
// Or you can cache its value when navigating to the products list each time
const params = React.useMemo(() => queryString(), []);
return <div>//</div>;
}
❌
// src/front-office/products/components/ProductsListPage.tsx
import { queryString } from "@mongez/react-router";
// if the url is something like https://site-name.com/online-store/products?sortBy=price&categoryId=10
console.log(queryString().toString()); // sortBy=price&categoryId=10
import React from "react";
const params = queryString();
export default function ProductsListPage() {
console.log(params.get("sortBy")); // empty string
return <div>//</div>;
}
Получение базового url проекта
Чтобы получить базовый url, импортируйте вспомогательную функцию baseUrl
, которая получит путь к домену с суффиксом basePath
, определенный в разделе конфигурации маршрутизатора.
import { baseUrl } from "@mongez/react-router";
console.log(baseUrl()); // something like https://sitename.com/online-store where /online-store is the basePath of the project.
Получить текущее направление страницы
Для получения текущего направления страницы используйте хелпер currentDirection
.
currentDirection(): string
import { currentDirection } from "@mongez/react-router";
console.log(currentDirection()); // ltr for example
Обратите внимание, что эта утилита зависит от свойства document.documentElement
dir
, если оно не установлено, то по умолчанию будет возвращено ltr
.
Direction Is
Проверяет, совпадает ли текущее направление с заданным, используя утилиту directionIs
.
directionIs(direction: 'ltr' | 'rtl'): boolean
import { directionIs } from "@mongez/react-router";
console.log(directionIs("ltr")); // true
console.log(directionIs("rtl")); // false
Получить предыдущий маршрут
Для получения предыдущего маршрута используйте функцию previousRoute
.
import { previousRoute, navigateTo } from "@mongez/react-router";
navigateTo("/login");
console.log(previousRoute()); // /
navigateTo("/");
console.log(previousRoute()); // /login
События маршрутизатора
Вы можете прослушать любое изменение маршрутизатора, основанное на изменении навигационной ссылки.
import { routerEvents } from "@mongez/react-router";
routerEvents.onChange(() => {
// route changed
});
Заключение
Основной целью пакета является написание хорошей системы маршрутизации структуры от базовых до сложных сценариев, я использовал его в десятках проектов до сих пор, и он работает как шарм.
Для получения дополнительной документации просмотрите репозиторий Github.
Не стесняйтесь присылать мне свои отзывы.