Skip to main content

Що таке layouts в Nuxt?

Layouts в Nuxt - це шаблони Vue, що обгортають вміст сторінок спільними елементами: шапкою, бічною панеллю або підвалом. Вміст сторінки рендериться всередині <slot /> у макеті.

Теорія

TL;DR

  • Макет (layout) схожий на каркас будівлі: стіни залишаються, а меблі (вміст сторінки) міняються від кімнати до кімнати.
  • Сторінки відповідають за контент; layouts керують спільним обрамленням навколо нього.
  • Якщо більше двох сторінок мають однаковий UI-каркас, layout позбавить від копіювання шапки по файлах.
  • Не вказав layout? Nuxt автоматично бере layouts/default.vue.

Короткий приклад

vue
<!-- layouts/default.vue --> <template> <div class="app"> <nav>Глобальна навігація</nav> <main> <slot /> <!-- тут рендериться вміст сторінки --> </main> <footer>© 2024</footer> </div> </template> <!-- pages/about.vue (мета не потрібна, застосовується default) --> <template> <p>Про нас</p> </template> <!-- Результат: nav + "Про нас" + footer -->

Сторінка about.vue нічого не знає про навігацію і підвал. Вона рендерить свій вміст, layout обгортає його.

Ключова різниця

Layouts відокремлюють постійний UI від контенту конкретної сторінки через <slot />. Сторінка /about автоматично рендериться всередині default.vue без жодного коду. Сторінка /admin/orders підключає admin.vue одним рядком у definePageMeta. Саме тому сторінки залишаються простими і зосередженими на своїх даних.

Коли використовувати

  • Спільні шапка і підвал на всіх сторінках: створи layouts/default.vue.
  • Адмін-панель з бічним меню: layouts/admin.vue + definePageMeta({ layout: 'admin' }) на кожній сторінці адміна.
  • Лендінги та сторінки застосунку: окремий layouts/landing.vue для маркетингу, layouts/app.vue для авторизованих користувачів.
  • Сторінка без обрамлення (повноекранний canvas, чистий маршрут): definePageMeta({ layout: false }).

Як Nuxt визначає layout

При збірці Nuxt сканує папку layouts/ і реєструє кожен файл за іменем. При навігації він перевіряє definePageMeta цільової сторінки. Якщо нічого не вказано, бере default. Немає default.vue - сторінка рендериться без обрамлення.

Я бачив у продакшені, як розробник видалив default.vue, вважаючи що він не використовується. Всі сторінки одразу втратили навігацію. Корисно пам'ятати.

Типові помилки

1. Забути layout: false на сторінках без обрамлення. Кожна сторінка без налаштування отримує default.vue. Повноекранна сторінка або чистий маршрут автоматично успадкує шапку і підвал.

vue
<script setup> definePageMeta({ layout: false }) </script>

2. Задати layout у змінній замість definePageMeta.

vue
<!-- Неправильно: ігнорується під час виконання --> <script setup> const layout = 'admin' // нічого не робить </script> <!-- Правильно --> <script setup> definePageMeta({ layout: 'admin' }) </script>

Тільки definePageMeta реєструє вибір layout. Змінні в <script setup> для цього не читаються.

3. Поставити <slot /> до шапки.

vue
<!-- Неправильно: вміст рендериться вище навігації --> <template> <slot /> <header>Навігація</header> </template> <!-- Правильно --> <template> <header>Навігація</header> <slot /> <footer>Підвал</footer> </template>

Порядок у шаблоні дорівнює порядку на екрані. Позиція <slot /> і є позицією контенту.

Де зустрічається в реальних проектах

  • Блог або документація: layouts/docs.vue з бічним змістом для всіх сторінок /blog/*.
  • Чекаут в e-commerce: layouts/checkout.vue з прогрес-баром, без основної навігації.
  • Адмін-панель: layouts/admin.vue з drawer для всіх маршрутів /admin/*.
  • Multi-tenant SaaS: різні layouts для різних клієнтів, перемикаються через middleware.

Питання на співбесіді

Q: Що станеться, якщо layouts/default.vue не існує?
A: Сторінка рендериться без обрамлення. Nuxt не кидає помилку.

Q: Чи може layout отримати дані зі сторінки всередині нього?
A: Напряму ні. Використовуй useState або useRoute() для спільного стану між layout і сторінкою.

Q: У чому різниця між layout: false і layout: 'none'?
A: Обидва скасовують layout. З Nuxt 3.6+ false є більш читабельним варіантом. Використовуй його.

Q: Як вкласти один layout в інший?
A: Нативної підтримки немає. Використовуй <NuxtLayout> всередині <slot /> батьківського layout як обхідне рішення.

Q: Якщо під час SSR layout не зміг отримати дані, що відбудеться зі сторінкою?
A: Сторінка рендериться в межах error boundary layout. Використовуй error.vue як резервний layout або <ClientOnly> для асинхронних даних layout.

Приклади

Базовий: default layout без налаштувань

vue
<!-- layouts/default.vue --> <template> <div class="page-wrapper"> <header>Мій застосунок</header> <slot /> <footer>Підвал</footer> </div> </template> <!-- pages/home.vue --> <template> <h1>Ласкаво просимо</h1> </template> <!-- Результат: шапка + "Ласкаво просимо" + підвал, без жодного налаштування -->

Кожна сторінка автоматично отримує default layout. definePageMeta не потрібен.

Проміжний: layout для дашборду на кількох сторінках

vue
<!-- layouts/dashboard.vue --> <template> <div class="dashboard"> <aside class="sidebar"> <NuxtLink to="/dashboard/orders">Замовлення</NuxtLink> <NuxtLink to="/dashboard/products">Товари</NuxtLink> </aside> <main class="content"> <slot /> </main> </div> </template> <!-- pages/dashboard/orders.vue --> <script setup> definePageMeta({ layout: 'dashboard' }) const { data: orders } = await useFetch('/api/orders') </script> <template> <ul> <li v-for="order in orders" :key="order.id"> Замовлення #{{ order.id }} - ${{ order.total }} </li> </ul> </template>

І /dashboard/orders, і /dashboard/products використовують одну бічну панель. При переходах між ними layout не перерендерюється. Міняється лише вміст <slot />.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.

Дочитали статтю?