Как реализовать масштабируемую систему маршрутизации с авторизацией и lazy loading?
Реализация масштабируемой системы маршрутизации с авторизацией и поддержкой lazy loading в React-приложении требует продуманной архитектуры, которая учитывает безопасность, производительность и удобство масштабирования. Основные компоненты такой системы включают:
1. Библиотека маршрутизации: react-router-dom
Актуальная версия React Router (v6+) предоставляет гибкий и мощный API:
-
Поддержка вложенных маршрутов.
-
Lazy-loading через React.lazy.
-
Layout-компоненты.
-
Guard-логика с element-обёртками.
-
Поддержка Loader, Action и ErrorBoundary.
2. Структура маршрутов
Маршруты можно конфигурировать в виде дерева или массива. Пример структуры:
src/
├── app/
│ ├── routes.tsx
│ ├── AppRouter.tsx
│ └── providers/
│
├── pages/
│ ├── Home/
│ ├── Dashboard/
│ ├── Login/
│ └── Admin/
│
├── features/
├── shared/
3. Ленивые загрузки (Lazy loading)
Используем React.lazy() и Suspense для динамической подгрузки компонентов страниц:
// AppRouter.tsx
import { lazy, Suspense } from "react";
import { Routes, Route } from "react-router-dom";
const HomePage = lazy(() => import("../pages/Home"));
const LoginPage = lazy(() => import("../pages/Login"));
const DashboardPage = lazy(() => import("../pages/Dashboard"));
const AdminPage = lazy(() => import("../pages/Admin"));
export function AppRouter() {
return (
<Suspense fallback={<div>Загрузка...</div>}>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/login" element={<LoginPage />} />
<Route path="/dashboard" element={
<ProtectedRoute>
<DashboardPage />
</ProtectedRoute>
} />
<Route path="/admin" element={
<RoleProtectedRoute role="admin">
<AdminPage />
</RoleProtectedRoute>
} />
</Routes>
</Suspense>
);
}
4. Авторизация и защита маршрутов (Route Guards)
Для защиты маршрутов создаются обёртки:
ProtectedRoute
import { Navigate, useLocation } from "react-router-dom";
import { useAuth } from "@/shared/hooks/useAuth";
export function ProtectedRoute({ children }: { children: JSX.Element }) {
const { isAuthenticated } = useAuth();
const location = useLocation();
if (!isAuthenticated) {
return <Navigate to="/login" state={{ from: location }} replace />;
}
return children;
}
RoleProtectedRoute
export function RoleProtectedRoute({
role,
children,
}: {
role: string;
children: JSX.Element;
}) {
const { isAuthenticated, user } = useAuth();
const location = useLocation();
if (!isAuthenticated || user.role !== role) {
return <Navigate to="/unauthorized" state={{ from: location }} replace />;
}
return children;
}
5. Хранение информации о пользователе
-
Токен аутентификации можно хранить в cookie или localStorage.
-
Данные пользователя (например, роль) — в глобальном состоянии (например, Redux или Context).
-
При запуске приложения данные подгружаются и записываются в хранилище:
useEffect(() => {
authService.getCurrentUser().then(setUser);
}, \[\]);
6. Разделение public/private маршрутов
Разделим маршруты логически:
const publicRoutes = \[
{ path: "/", element: <HomePage /> },
{ path: "/login", element: <LoginPage /> },
\];
const privateRoutes = \[
{
path: "/dashboard",
element: (
<ProtectedRoute>
<DashboardPage />
</ProtectedRoute>
),
},
{
path: "/admin",
element: (
<RoleProtectedRoute role="admin">
<AdminPage />
</RoleProtectedRoute>
),
},
\];
Можно объединить их в AppRouter с помощью .map.
7. Layout и вложенные маршруты
React Router поддерживает layout-компоненты:
<Route element={<MainLayout />}>
<Route path="/" element={<HomePage />} />
<Route path="/dashboard" element={<DashboardPage />} />
</Route>
В MainLayout может быть Sidebar, Navbar, Outlet.
8. Error Boundary и fallback
Для обработки ошибок загрузки можно использовать Suspense + ErrorBoundary:
<Suspense fallback={<LoadingScreen />}>
<ErrorBoundary fallback={<ErrorFallback />}>
<Routes>...</Routes>
</ErrorBoundary>
</Suspense>
9. Поддержка роли, языка и параметров
URL можно использовать с параметрами:
<Route path="/profile/:id" element={<ProfilePage />} />
А в компоненте:
const { id } = useParams();
Для локализации маршрутов можно использовать i18n + конфиг pathMap или поддомены.
10. Тестирование маршрутов
Для unit-тестов:
import { MemoryRouter } from "react-router-dom";
render(
<MemoryRouter initialEntries={\["/dashboard"\]}>
<AppRouter />
</MemoryRouter>
);
Такой подход позволяет проверять защищённость маршрутов, редиректы и lazy-загрузку.
11. Автоматическая генерация маршрутов (опционально)
В больших приложениях можно создавать маршруты автоматически из структуры файлов:
const pages = import.meta.glob("../pages/\*\*/\*.tsx");
const routes = Object.entries(pages).map((\[path, loader\]) => ({
path: pathToRoute(path), // преобразовать путь в роут
element: lazy(loader),
}));
Это снижает рутину при масштабировании.
12. SSR/SPA/CSR поддержка
Если приложение использует SSR (например, Next.js), механика авторизации и маршрутов может отличаться (middleware, getServerSideProps и т.п.).
Для SPA используется BrowserRouter, а при необходимости поддержки старых браузеров или hash-based маршрутов — HashRouter.
13. Хуки: useNavigate, useLocation, useParams
React Router предоставляет мощные хуки:
-
useNavigate() — программный переход.
-
useLocation() — текущий URL + state.
-
useParams() — параметры URL.
-
useSearchParams() — query-параметры.
Пример:
const navigate = useNavigate();
useEffect(() => {
if (user.role === "admin") {
navigate("/admin");
}
}, \[user\]);
14. Управление доступом и редиректы при авторизации
После логина можно делать редирект:
const location = useLocation();
const from = location.state?.from?.pathname || "/";
login().then(() => {
navigate(from, { replace: true });
});
Это позволяет возвращаться на страницу, которую пользователь изначально хотел открыть.