Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Як додати резервний контент для слотів у Vue?». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Fallback content** слота - це шаблон всередині `<slot>`, який відображається коли батьківський компонент не передає свій контент. ```vue <template> <button> <slot>Натисни мене</slot> </button> </template> <Button /> <!-- Показує: Натисни мене --> <Button>Зберегти</Button> <!-- Показує: Зберегти --> ``` **Ключове:** контент від батька повністю замінює fallback, без злиття.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Fallback content** для слота - це шаблон всередині тегу `<slot>`, який відображається коли батьківський компонент не передає власний контент для цього слота. ## Теорія ### TL;DR - Уявіть поштову скриньку: fallback - це табличка "Пошти немає", яка з'являється коли нічого не доставили - Будь-який контент всередині `<slot>` стає резервним за замовчуванням - Контент від батьківського компонента повністю замінює fallback. Вони не об'єднуються - Працює з default та іменованими слотами однаково - Підходить для опціональних секцій UI: порожні стани, стандартні заголовки, загальні повідомлення ### Швидкий приклад ```vue <!-- Button.vue --> <template> <button class="btn"> <slot>Натисни мене</slot> <!-- резервний текст --> </button> </template> <!-- Без контенту: відображає "Натисни мене" --> <Button /> <!-- З контентом: відображає "Зберегти" --> <Button>Зберегти</Button> ``` Дочірній компонент визначає що показати, якщо батько нічого не передав. Батько може перевизначити це, але не зобов'язаний. ### Як Vue вирішує що рендерити Vue перевіряє чи передав батьківський компонент контент для конкретного слота. Якщо так - рендерить його. Якщо ні - використовує внутрішній шаблон слота. Жодного змішування: одне перемагає, інше зникає повністю. Це відбувається під час монтування компонента. Компілятор шаблонів генерує умовну перевірку: якщо `slots.default` існує (або `slots.header` для іменованих слотів) - рендерить його, інакше рендерить fallback-елементи. Жодних додаткових витрат під час виконання, крім звичайного diffing vnode. ### Коли використовувати - Опціональні секції макету (бічні панелі, заголовки) де більшість сторінок передає контент, але деякі ні - Компоненти сповіщень і алертів де загальне повідомлення покриває типовий випадок - Обгортки для кнопок і полів введення з розумними мітками за замовчуванням - Не використовуй fallback для слотів, які завжди заповнюються з батька. Для обов'язкового контенту краще props Більшість дашбордів, з якими я працював, використовують fallback іменованих слотів саме так: компонент макету оголошує безпечні значення за замовчуванням, а окремі сторінки перевизначають тільки те що потрібно. ### Типові помилки **Очікування що fallback зіллється з контентом батька** ```vue <!-- Неправильне очікування --> <ChildComponent> <p>Додатковий абзац</p> </ChildComponent> <!-- Показується тільки "Додатковий абзац". Fallback зник. --> ``` Слоти замінюють повністю. Якщо потрібно і резервний контент і додатковий - визнач окремі іменовані слоти. **Неправильна назва іменованого слота** ```vue <!-- Дочірній оголошує: <slot name="header"> --> <!-- Батько використовує неправильну назву --> <template #head>Кастомний заголовок</template> <!-- "head" ніколи не збігається з "header". Fallback завжди показується. --> ``` Назви чутливі до регістру і мають збігатися точно. Default-слот назви не має. **Порожній template обманює перевірку fallback** ```vue <ChildComponent> <template #default></template> </ChildComponent> <!-- Vue бачить що слот передано. Fallback не відображається. --> ``` Vue вважає будь-який переданий слот заповненим, навіть порожній. Це ловить тих, хто намагається умовно передавати слоти з батьківського компонента. **Динамічні fallback і SSR** ```vue <!-- Ризиковано в Nuxt --> <slot><div v-if="isClient">Fallback</div></slot> ``` Якщо fallback залежить від стану тільки на клієнті, виникне помилка гідратації: сервер нічого не рендерить, клієнт додає div. Тримай fallback статичними або обгортай в `<ClientOnly>`. ### Де використовується - Vuetify `v-card`: fallback для заголовка і блоку дій коли батько їх не передає - Element Plus `el-table`: повідомлення про порожній стан через fallback слота - Quasar `q-page`: стандартні заголовки і бічні панелі з брендуванням додатка - Nuxt UI: fallback-сповіщення в слотах глобального макету ### Питання на співбесіді **Q:** Чи може fallback контент звертатись до даних дочірнього компонента? **A:** Так. Fallback живе в scope шаблону дочірнього компонента, тому має повний доступ до його реактивних даних, обчислюваних властивостей і методів. **Q:** Що станеться якщо батько передасть контент для слота якого немає в дочірньому? **A:** Vue його ігнорує. Дочірній компонент має явно оголосити `<slot name="foo">`, інакше контент зникне без жодного попередження. **Q:** Чи працюють scoped slots з fallback? **A:** Так, але є нюанс. Fallback всередині scoped slot може звертатись до даних що слот передає, бо виконується в scope дочірнього компонента. Але якщо батько перевизначає слот, він має використати `v-slot="{ items }"` щоб отримати ці дані. Fallback в такому разі взагалі не виконується. **Q:** Чи є різниця в продуктивності між fallback і звичайним контентом? **A:** Жодної суттєвої. Перевірка - це умовна логіка на етапі компіляції. Обидва шляхи генерують однакову кількість vnode-операцій. ## Приклади ### Кнопка з міткою за замовчуванням ```vue <!-- SubmitButton.vue --> <template> <button type="submit" class="btn-primary"> <slot>Відправити</slot> </button> </template> <!-- Використання --> <SubmitButton /> <!-- Відображає: Відправити --> <SubmitButton>Зберегти чернетку</SubmitButton> <!-- Відображає: Зберегти чернетку --> <SubmitButton>Видалити</SubmitButton> <!-- Відображає: Видалити --> ``` Один компонент, різні мітки, жодних додаткових props. Fallback покриває типовий випадок, а там де потрібно щось інше - перевизначають при виклику. ### Макет дашборду з іменованими слотами ```vue <!-- AppLayout.vue --> <template> <div class="layout"> <header> <slot name="header"> <h1>Мій Додаток</h1> <!-- загальний fallback --> </slot> </header> <aside> <slot name="sidebar"> <p>Бічна панель не налаштована</p> </slot> </aside> <main> <slot>Завантажте свій контент тут</slot> </main> </div> </template> <!-- CRM-сторінка: перевизначає header і sidebar, залишає main як fallback --> <AppLayout> <template #header> <h1>Дашборд клієнтів</h1> </template> <template #sidebar> <nav>Користувачі | Звіти | Оплата</nav> </template> <!-- #default не передано: main показує резервний текст --> </AppLayout> ``` Кожен слот незалежний. Передача контенту для `#header` не впливає на `#sidebar` чи default-слот. Fallback заповнює тільки ті прогалини, які батько лишив відкритими.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.