17. Роутинг: @solidjs/router
🗺️ Роутинг в Solid.js: @solidjs/router
Заголовок раздела «🗺️ Роутинг в Solid.js: @solidjs/router»Яша, роутинг в Solid — это @solidjs/router, и он здорово продуман! Поддержка вложенных маршрутов, data loaders, ленивой загрузки и даже route guards. Главное отличие от React Router — загрузчики данных (data()) интегрированы в роутер, а не вынесены в useEffect. Поехали разбираться! 🚀
📦 Установка и базовая настройка
Заголовок раздела «📦 Установка и базовая настройка»npm install @solidjs/routerimport { render } from 'solid-js/web';import { Router } from '@solidjs/router';import App from './App';
render( () => ( <Router> <App /> </Router> ), document.getElementById('root')!);🔀 Базовые маршруты
Заголовок раздела «🔀 Базовые маршруты»import { Route, Router } from '@solidjs/router';import Home from './pages/Home';import About from './pages/About';import NotFound from './pages/NotFound';
function App() { return ( <Router> <Route path="/" component={Home} /> <Route path="/about" component={About} /> <Route path="*404" component={NotFound} /> </Router> );}🔗 Компонент A (аналог Link)
Заголовок раздела «🔗 Компонент A (аналог Link)»import { A } from '@solidjs/router';
function Nav() { return ( <nav> {/* A автоматически добавляет класс active при совпадении маршрута */} <A href="/" activeClass="active" end>Главная</A> <A href="/about" activeClass="active">О нас</A> <A href="/users" activeClass="active">Пользователи</A> </nav> );}
// CSS// a.active { color: #2c67d5; font-weight: bold; }💡
end— точное совпадение. Без него/будет активен на всех страницах, так как/aboutтоже начинается с/.
🔧 Программная навигация
Заголовок раздела «🔧 Программная навигация»import { useNavigate, useLocation } from '@solidjs/router';
function LoginForm() { const navigate = useNavigate(); const location = useLocation();
const handleLogin = async (data: LoginData) => { await login(data); // Возвращаемся туда, откуда пришли (или на главную) const from = location.query.from || '/dashboard'; navigate(from, { replace: true }); };
return ( <form onSubmit={handleLogin}> {/* ... */} </form> );}📦 Параметры маршрута
Заголовок раздела «📦 Параметры маршрута»import { useParams } from '@solidjs/router';
// Route: /users/:idfunction UserProfile() { const params = useParams<{ id: string }>();
// params.id — реактивный! const [user] = createResource( () => params.id, // источник (ключ кэша) (id) => fetchUser(id) );
return ( <Show when={user()} fallback={<p>Загрузка...</p>}> {(u) => <h1>{u().name}</h1>} </Show> );}🔍 Search Params (Query String)
Заголовок раздела «🔍 Search Params (Query String)»import { useSearchParams } from '@solidjs/router';
function SearchPage() { const [searchParams, setSearchParams] = useSearchParams<{ q: string; page: string; category: string; }>();
return ( <div> <input value={searchParams.q || ''} onInput={(e) => setSearchParams({ q: e.target.value, page: '1' })} placeholder="Поиск..." /> <select value={searchParams.category || 'all'} onChange={(e) => setSearchParams({ category: e.target.value })} > <option value="all">Все</option> <option value="books">Книги</option> <option value="movies">Фильмы</option> </select> <p>Поиск: {searchParams.q}, Страница: {searchParams.page || '1'}</p> </div> );}🏗️ Вложенные маршруты
Заголовок раздела «🏗️ Вложенные маршруты»// Макет с общим менюfunction DashboardLayout() { return ( <div class="dashboard"> <aside> <A href="/dashboard">Обзор</A> <A href="/dashboard/profile">Профиль</A> <A href="/dashboard/settings">Настройки</A> </aside> <main> {/* Вложенные маршруты рендерятся здесь */} <Outlet /> </main> </div> );}
// Роутерfunction App() { return ( <Router> <Route path="/" component={Home} /> <Route path="/dashboard" component={DashboardLayout}> {/* Вложенные маршруты */} <Route path="/" component={DashboardHome} /> <Route path="/profile" component={Profile} /> <Route path="/settings" component={Settings} /> </Route> </Router> );}⚡ Ленивая загрузка маршрутов
Заголовок раздела «⚡ Ленивая загрузка маршрутов»import { lazy } from 'solid-js';import { Route, Router } from '@solidjs/router';
// Компоненты загружаются только при переходе на маршрутconst Home = lazy(() => import('./pages/Home'));const Dashboard = lazy(() => import('./pages/Dashboard'));const Settings = lazy(() => import('./pages/Settings'));
function App() { return ( <Router> <Suspense fallback={<LoadingSpinner />}> <Route path="/" component={Home} /> <Route path="/dashboard" component={Dashboard} /> <Route path="/settings" component={Settings} /> </Suspense> </Router> );}📥 Data Loaders (новый API)
Заголовок раздела «📥 Data Loaders (новый API)»// Загрузчик данных — запускается ДО рендера компонентаimport { createAsync, type RouteDefinition } from '@solidjs/router';
export const route = { load: ({ params }) => getUserData(params.id), // предзагрузка} satisfies RouteDefinition;
function UserPage() { const params = useParams<{ id: string }>(); // createAsync использует данные из load() — уже кэшированы! const user = createAsync(() => getUserData(params.id));
return ( <Suspense fallback={<Skeleton />}> <h1>{user()?.name}</h1> </Suspense> );}🛡️ Route Guards
Заголовок раздела «🛡️ Route Guards»import { Navigate } from '@solidjs/router';
function ProtectedRoute(props: { component: Component }) { const [auth] = useContext(AuthContext);
return ( <Show when={auth.isLoggedIn} fallback={<Navigate href="/login?from=/dashboard" />} > <props.component /> </Show> );}
// Использование<Route path="/dashboard" component={() => <ProtectedRoute component={Dashboard} />}/>