9. Лейауты
Лейауты (layouts) в Qwik City — это компоненты, которые оборачивают страницы и применяются автоматически на основе файловой структуры. Они позволяют создавать общую шапку, навигацию и подвал для группы страниц.
Базовый лейаут
Заголовок раздела «Базовый лейаут»Файл layout.tsx в директории routes/ автоматически применяется ко всем страницам в этой директории:
// src/routes/layout.tsx — применяется ко всем страницамimport { component$, Slot } from '@builder.io/qwik';
export default component$(() => { return ( <div class="page"> <header> <nav> <a href="/">Главная</a> <a href="/about">О нас</a> </nav> </header>
<main> <Slot /> {/* Здесь рендерится содержимое страницы */} </main>
<footer> <p>© 2024 Мой сайт</p> </footer> </div> );});Компонент Slot
Заголовок раздела «Компонент Slot»<Slot /> — это место для вставки дочернего содержимого. Аналог {children} в React, но работает декларативно:
// Лейаутexport const Card = component$(() => ( <div class="card"> <Slot name="header" /> {/* Именованный слот */} <div class="card-body"> <Slot /> {/* Дефолтный слот */} </div> <Slot name="footer" /> {/* Именованный слот */} </div>));
// Использованиеexport const Page = component$(() => ( <Card> <div q:slot="header"> <h2>Заголовок карточки</h2> </div>
<p>Основной контент</p>
<div q:slot="footer"> <button>Действие</button> </div> </Card>));Вложенные лейауты
Заголовок раздела «Вложенные лейауты»Лейауты можно вкладывать — каждый уровень директории может иметь свой лейаут:
src/routes/├── layout.tsx → Корневой лейаут (Header + Footer)├── index.tsx → / (обёрнут в корневой)├── blog/│ ├── layout.tsx → Лейаут блога (Sidebar)│ ├── index.tsx → /blog (корневой + блог)│ └── [slug]/│ └── index.tsx → /blog/:slug (корневой + блог)└── dashboard/ ├── layout.tsx → Лейаут дашборда (Sidebar + Auth) └── index.tsx → /dashboard (корневой + дашборд)import { component$, Slot } from '@builder.io/qwik';
export default component$(() => { return ( <div class="blog-layout"> <aside class="sidebar"> <h3>Категории</h3> <ul> <li>JavaScript</li> <li>Qwik</li> </ul> </aside> <div class="blog-content"> <Slot /> </div> </div> );});Лейаут с данными (routeLoader$)
Заголовок раздела «Лейаут с данными (routeLoader$)»Лейауты могут загружать данные через routeLoader$:
import { component$, Slot } from '@builder.io/qwik';import { routeLoader$ } from '@builder.io/qwik-city';
export const useCurrentUser = routeLoader$(async ({ cookie }) => { const token = cookie.get('auth'); if (!token) return null; return await getUserFromToken(token.value);});
export default component$(() => { const user = useCurrentUser();
return ( <div> <header> {user.value ? ( <span>Привет, {user.value.name}!</span> ) : ( <a href="/login">Войти</a> )} </header> <Slot /> </div> );});Отключение лейаута (named layout)
Заголовок раздела «Отключение лейаута (named layout)»Если страница не должна использовать стандартный лейаут, можно использовать именованный файл [email protected]:
src/routes/├── layout.tsx → Основной лейаут├── login/│ ├── [email protected] → Специальный лейаут для авторизации│ └── [email protected] → /login (использует layout@auth)Error Boundary
Заголовок раздела «Error Boundary»Лейауты могут содержать обработчики ошибок:
// Экспортируй ErrorBoundary из лейаутаexport const ErrorBoundary = component$(() => { const err = useErrorBoundary(); return ( <div class="error"> <h1>Что-то пошло не так</h1> <p>{err.message}</p> </div> );});