Що таке layouts в Nuxt?
Layouts в Nuxt - це шаблони Vue, що обгортають вміст сторінок спільними елементами: шапкою, бічною панеллю або підвалом. Вміст сторінки рендериться всередині <slot /> у макеті.
Теорія
TL;DR
- Макет (layout) схожий на каркас будівлі: стіни залишаються, а меблі (вміст сторінки) міняються від кімнати до кімнати.
- Сторінки відповідають за контент; layouts керують спільним обрамленням навколо нього.
- Якщо більше двох сторінок мають однаковий UI-каркас, layout позбавить від копіювання шапки по файлах.
- Не вказав layout? Nuxt автоматично бере
layouts/default.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. Повноекранна сторінка або чистий маршрут автоматично успадкує шапку і підвал.
<script setup>
definePageMeta({ layout: false })
</script>2. Задати layout у змінній замість definePageMeta.
<!-- Неправильно: ігнорується під час виконання -->
<script setup>
const layout = 'admin' // нічого не робить
</script>
<!-- Правильно -->
<script setup>
definePageMeta({ layout: 'admin' })
</script>Тільки definePageMeta реєструє вибір layout. Змінні в <script setup> для цього не читаються.
3. Поставити <slot /> до шапки.
<!-- Неправильно: вміст рендериться вище навігації -->
<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 без налаштувань
<!-- 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 для дашборду на кількох сторінках
<!-- 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 />.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.