Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Що таке layouts в Nuxt?». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Layouts в Nuxt** - це Vue-шаблони в папці `layouts/`, що обгортають сторінки спільним UI: шапкою, підвалом або бічним меню. `layouts/default.vue` застосовується до всіх сторінок автоматично. Для іншого layout додай `definePageMeta({ layout: 'назва' })`. Вміст сторінки рендериться всередині `<slot />`. **Ключове:** layouts виносять спільний UI з окремих файлів сторінок.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**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 />`.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.